Flutter剪贴板管理插件super_clipboard的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter剪贴板管理插件super_clipboard的使用

概述

super_clipboard 是一个为Flutter应用程序提供全面剪贴板功能的插件,支持macOS、iOS、Android、Windows、Linux和Web平台。它允许开发者以平台无关的方式读取和写入常见的剪贴板格式,并支持自定义数据格式。

super_clipboard

功能特性

  • 全面的剪贴板功能:支持多种平台上的剪贴板操作。
  • 多平台支持:适用于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

1 回复

更多关于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插件的基本用法,包括复制、粘贴和监听剪贴板内容的变化。你可以根据需要进一步扩展和自定义这些功能。

回到顶部