Flutter剪贴板管理插件super_clipboard的使用
Flutter剪贴板管理插件super_clipboard的使用
概述
super_clipboard
是一个为Flutter应用程序提供全面剪贴板功能的插件,支持macOS、iOS、Android、Windows、Linux和Web平台。它允许开发者以平台无关的方式读取和写入常见的剪贴板格式,并支持自定义数据格式。
功能特性
- 全面的剪贴板功能:支持多种平台上的剪贴板操作。
- 多平台支持:适用于macOS、iOS、Android、Windows、Linux和Web。
- 平台无关代码:可以读写常见的剪贴板格式。
- 自定义数据格式:支持自定义数据格式。
- 多表示形式:支持多个表示形式的剪贴板项。
- 按需提供剪贴板数据:可以在需要时提供剪贴板数据。
入门指南
Rust安装
super_clipboard
使用Rust实现底层平台特定的功能。如果未安装Rust,插件会自动下载预编译的二进制文件。为了从源码编译Rust代码,可以通过rustup安装Rust。
macOS或Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Windows
可以使用Rust Installer。
确保Rust是最新版本:
rustup update
Android支持
NDK是必需的,如果没有安装,会在第一次构建时自动安装。NDK版本在android/app/build.gradle
中指定。
对于较旧的Flutter Android项目,需要在android/app/build.gradle
中指定最小SDK版本:
android {
defaultConfig {
minSdkVersion 23
}
}
要将图像和其他自定义数据写入Android剪贴板,需要在AndroidManifest.xml
中声明内容提供者:
<manifest>
<application>
...
<provider
android:name="com.superlist.super_native_extensions.DataProvider"
android:authorities="<your-package-name>.SuperClipboardDataProvider"
android:exported="true"
android:grantUriPermissions="true" />
...
</application>
</manifest>
请替换<your-package-name>
为实际的包名。
使用方法
从剪贴板读取
import 'package:super_clipboard/super_clipboard.dart';
final clipboard = SystemClipboard.instance;
if (clipboard == null) {
return; // 剪贴板API不支持此平台。
}
final reader = await clipboard.read();
if (reader.canProvide(Formats.htmlText)) {
final html = await reader.readValue(Formats.htmlText);
// 处理HTML文本
}
if (reader.canProvide(Formats.plainText)) {
final text = await reader.readValue(Formats.plainText);
// 处理纯文本
}
if (reader.canProvide(Formats.png)) {
reader.getFile(Formats.png, (file) {
final stream = file.getStream();
// 处理PNG图像流
});
}
写入剪贴板
import 'package:super_clipboard/super_clipboard.dart';
final clipboard = SystemClipboard.instance;
if (clipboard == null) {
return; // 剪贴板API不支持此平台。
}
final item = DataWriterItem();
item.add(Formats.htmlText('<b>HTML text</b>'));
item.add(Formats.plainText('plain text'));
item.add(Formats.png(imageData));
await clipboard.write([item]);
按需提供数据
final item = DataWriterItem();
item.add(Formats.htmlText.lazy(() => '<b>HTML text</b>'));
item.add(Formats.plainText.lazy(() => 'plain text'));
item.add(Formats.png.lazy(() => imageData));
await clipboard.write([item]);
Web浏览器访问剪贴板
final events = ClipboardEvents.instance;
if (events == null) {
// Web剪贴板事件仅支持Web平台。
return;
}
events.registerPasteEventListener((event) async {
final reader = await event.getClipboardReader();
if (reader.canProvide(Formats.htmlText)) {
final html = await event.clipboardReader.readValue(Formats.htmlText);
// 处理HTML文本
}
});
events.registerCopyEventListener((event) {
final item = DataWriterItem();
item.add(Formats.htmlText('<b>HTML text</b>'));
item.add(Formats.plainText('plain text'));
await event.write([item]);
});
示例代码
以下是完整的示例代码,展示了如何使用super_clipboard
进行剪贴板操作:
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'package:flutter_layout_grid/flutter_layout_grid.dart';
void main() async {
runApp(const MyApp());
}
const _notAvailableMessage =
'Clipboard is not available on this platform. Use ClipboardEvents API instead.';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SuperClipboard Example',
theme: ThemeData(
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 16)),
),
primarySwatch: Colors.blue,
useMaterial3: false,
),
home: const MyHomePage(title: 'SuperClipboard Example'),
);
}
}
class Expand extends SingleChildRenderObjectWidget {
const Expand({super.key, required super.child});
@override
RenderObject createRenderObject(BuildContext context) => _RenderExpanded();
}
class _RenderExpanded extends RenderProxyBox {
@override
void layout(Constraints constraints, {bool parentUsesSize = false}) {
final boxConstraints = constraints as BoxConstraints;
super.layout(
boxConstraints.tighten(
width: boxConstraints.maxWidth,
height: boxConstraints.maxHeight,
),
parentUsesSize: parentUsesSize);
}
}
class HomeLayout extends StatelessWidget {
const HomeLayout({
super.key,
required this.mainContent,
required this.buttons,
});
final List<Widget> mainContent;
final List<Widget> buttons;
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < 540) {
return ListView(
padding: const EdgeInsets.all(16),
children: [
LayoutGrid(
autoPlacement: AutoPlacement.rowDense,
columnSizes: [1.5.fr, 2.fr],
rowSizes: const [auto, auto, auto, auto],
gridFit: GridFit.expand,
rowGap: 10,
columnGap: 10,
children: buttons.map((e) => Expand(child: e)).toList(),
),
const SizedBox(height: 16),
...mainContent,
],
);
} else {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: buttons
.intersperse(const SizedBox(height: 10))
.toList(growable: false),
),
),
),
),
VerticalDivider(
color: Colors.blueGrey.shade100,
thickness: 1,
width: 1,
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: mainContent,
),
)
],
);
}
});
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
void showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(milliseconds: 1500),
),
);
}
@override
void initState() {
super.initState();
ClipboardEvents.instance?.registerPasteEventListener(_onPasteEvent);
}
@override
void dispose() {
super.dispose();
ClipboardEvents.instance?.unregisterPasteEventListener(_onPasteEvent);
}
void copyText() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem();
item.add(Formats.htmlText('<b>This is a <em>HTML</em> value</b>.'));
item.add(Formats.plainText('This is a plaintext value.'));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyTextLazy() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem();
item.add(Formats.htmlText.lazy(() {
showMessage('Lazy rich text requested.');
return '<b>This is a <em>HTML</em> value</b> generated <u>on demand</u>.';
}));
item.add(Formats.plainText.lazy(() {
showMessage('Lazy plain text requested.');
return 'This is a plaintext value generated on demand.';
}));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyImage() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final image = await createImageData(Colors.red);
final item = DataWriterItem(suggestedName: 'RedCircle.png');
item.add(Formats.png(image));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyImageLazy() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem(suggestedName: 'BlueCircle.png');
item.add(Formats.png.lazy(() {
showMessage('Lazy image requested.');
return createImageData(Colors.blue);
}));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyCustomData() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem();
item.add(formatCustom(Uint8List.fromList([1, 2, 3, 4])));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyCustomDataLazy() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem();
item.add(formatCustom.lazy(() async {
showMessage('Lazy custom data requested.');
return Uint8List.fromList([1, 2, 3, 4, 5, 6]);
}));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void copyUri() async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final item = DataWriterItem();
item.add(Formats.uri(NamedUri(
Uri.parse('https://github.com/superlistapp/super_native_extensions'),
name: 'Super Native Extensions')));
await clipboard.write([item]);
} else {
showMessage(_notAvailableMessage);
}
}
void _paste(ClipboardReader reader) async {
final readers = await Future.wait(
reader.items.map((e) => ReaderInfo.fromReader(e)),
);
if (!mounted) {
return;
}
buildWidgetsForReaders(context, readers, (widgets) {
setState(() {
contentWidgets = widgets;
});
});
}
void _onPasteEvent(ClipboardReadEvent event) async {
_paste(await event.getClipboardReader());
}
var contentWidgets = <Widget>[];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: HomeLayout(
mainContent: contentWidgets
.intersperse(const SizedBox(height: 10))
.toList(growable: false),
buttons: [
OutlinedButton(
onPressed: copyText,
child: const Text('Copy Text'),
),
OutlinedButton(
onPressed: copyTextLazy, child: const Text('Copy Text - Lazy')),
OutlinedButton(onPressed: copyImage, child: const Text('Copy Image')),
OutlinedButton(
onPressed: copyImageLazy, child: const Text('Copy Image - Lazy')),
OutlinedButton(
onPressed: copyCustomData, child: const Text('Copy Custom')),
OutlinedButton(
onPressed: copyCustomDataLazy,
child: const Text('Copy Custom - Lazy')),
OutlinedButton(onPressed: copyUri, child: const Text('Copy URI')),
OutlinedButton(
onPressed: () async {
final clipboard = SystemClipboard.instance;
if (clipboard != null) {
final reader = await clipboard.read();
_paste(reader);
} else {
showMessage(_notAvailableMessage);
}
},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: const Text('Paste')),
],
),
);
}
}
Future<Uint8List> createImageData(Color color) async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final paint = Paint()..color = color;
canvas.drawOval(const Rect.fromLTWH(0, 0, 200, 200), paint);
final picture = recorder.endRecording();
final image = await picture.toImage(200, 200);
final data = await image.toByteData(format: ui.ImageByteFormat.png);
return data!.buffer.asUint8List();
}
以上代码展示了如何使用super_clipboard
插件来处理不同类型的剪贴板操作,包括文本、图像、自定义数据等。通过这些示例,您可以更好地理解如何在Flutter应用中集成和使用该插件。
更多关于Flutter剪贴板管理插件super_clipboard的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter剪贴板管理插件super_clipboard的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用super_clipboard
插件来管理剪贴板的代码示例。super_clipboard
插件提供了更多高级功能,比如复制、粘贴以及监听剪贴板内容的变化。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加super_clipboard
依赖:
dependencies:
flutter:
sdk: flutter
super_clipboard: ^x.y.z # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
2. 导入插件
在你的Dart文件中导入super_clipboard
插件:
import 'package:super_clipboard/super_clipboard.dart';
import 'package:flutter/material.dart';
3. 使用插件功能
下面是一个简单的Flutter应用示例,展示了如何使用super_clipboard
插件来复制文本到剪贴板、从剪贴板粘贴文本以及监听剪贴板内容的变化。
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ClipboardManagerPage(),
);
}
}
class ClipboardManagerPage extends StatefulWidget {
@override
_ClipboardManagerPageState createState() => _ClipboardManagerPageState();
}
class _ClipboardManagerPageState extends State<ClipboardManagerPage> {
String? clipboardContent = "";
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
// 监听剪贴板内容的变化
SuperClipboard.addListener(() async {
String? newContent = await SuperClipboard.getText();
if (newContent != clipboardContent) {
setState(() {
clipboardContent = newContent;
});
}
});
}
@override
void dispose() {
// 移除监听器
SuperClipboard.removeListener();
super.dispose();
}
void _copyToClipboard() async {
String textToCopy = _controller.text;
await SuperClipboard.setText(textToCopy);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Copied to clipboard: $textToCopy")),
);
}
void _pasteFromClipboard() async {
String? text = await SuperClipboard.getText();
if (text != null) {
_controller.text = text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Pasted from clipboard: $text")),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Clipboard Manager'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter text to copy',
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _copyToClipboard,
child: Text('Copy to Clipboard'),
),
SizedBox(height: 8),
ElevatedButton(
onPressed: _pasteFromClipboard,
child: Text('Paste from Clipboard'),
),
SizedBox(height: 32),
Text(
'Current Clipboard Content:',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
clipboardContent ?? 'No content',
style: TextStyle(color: Colors.grey),
),
],
),
),
);
}
}
4. 运行应用
保存所有文件并运行你的Flutter应用。你现在应该能够看到一个简单的界面,允许你复制文本到剪贴板、从剪贴板粘贴文本,并且实时显示当前剪贴板的内容。
这个示例展示了super_clipboard
插件的基本用法,包括复制、粘贴和监听剪贴板内容的变化。你可以根据需要进一步扩展和自定义这些功能。