対象者
- Flutterを用いたアプリ開発経験があり、機能拡張を目指している開発者
- ユーザーエクスペリエンスを向上させるためのクリップボード操作に関心がある中級者
- 効率的なコード実装とベストプラクティスを追求するプログラマ
はじめに
日常的なモバイルアプリ開発では、「テキストのコピー」や「ペースト」を実装するシーンに直面することが多々あります。ユーザーがアプリ内で生成したコンテンツを、簡単に他アプリへ持ち運んだり、逆に他アプリから取り込んだりするためにはクリップボード機能が欠かせません。本記事では、Flutterにおけるクリップボード操作を、シンプルな例を通して解説します。
クリップボード操作の基本
Flutterでクリップボードを扱うためには、package:flutter/services.dart
を利用します。Clipboard
クラスが用意されており、setData
とgetData
メソッドを通じて、テキストなどをシステムクリップボードに書き込んだり、読み出したりすることが可能です。
コピー(setData)
ユーザーがボタンを押した際に任意のテキストをクリップボードへ書き込みます。
Clipboard.setData(const ClipboardData(text: 'コピーされたテキスト'));
右クリックのメニューから「コピー」する分には不要です。
ペースト(getData)
クリップボード上のテキストデータを取得します。
final data = await Clipboard.getData('text/plain');
setState(() {
_copiedText = data?.text ?? 'データなし';
});
ユーザーへのフィードバック
コピー操作が成功した際にToastやSnackBarで「コピーしました」とユーザーに通知すると、操作性と信頼性が向上します。また、ペースト時には、過去にコピーしたテキストがない場合「データなし」と表示するなど、ユーザーが状況を理解しやすいようにすることが大切です。
HTMLのペースト
標準のClipboard
では、テキストとしてはペーストできましたが、HTMLをフォーマット付きではペーストできませんでした。そこでsuper_clipboard
を使用したら、 HTMLをペーストできました。
PNG(画像全般?)でもペーストできるそうです。
super_clipboardのインストール
flutter pub add super_clipboard
htmlのペースト処理
final clipboard = SystemClipboard.instance;
if (clipboard == null) {
return;
}
final reader = await clipboard.read();
if (reader.canProvide(Formats.htmlText)) {
final html = await reader.readValue(Formats.htmlText);
setState(() {
if (html == null) {
_copiedText = 'データなし';
} else {
_copiedText = html;
}
});
}
その他のパッケージ
試してませんが、以下のようなパッケージがありました。
クリップボードの中身を変更を検知する
まとめ
Flutterでのクリップボード操作は、Clipboard
クラスを用いた単純なAPIコールで実現できます。しかし、単なるコピー&ペースト処理にとどまらず、ユーザーへのフィードバックを考慮することで、ユーザーが違和感なく情報を行き来でき、快適なユーザーエクスペリエンスの実現できます。
また標準外のパッケージでHTMLのペーストができました。
参考
super_clipboard
- clipboard_watcher
- 【Flutter】TextInputFormatterを使って数値をフォーマット
そういえば、コピペの記事書いてないな、と気づく。
ソース(main.dartにコピペして動作確認用)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:super_clipboard/super_clipboard.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _copiedText = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: [
FilledButton(
onPressed: () {
Clipboard.setData(const ClipboardData(text: 'コピーされたテキスト'));
},
child: const Text('コピー')),
FilledButton(
onPressed: () async {
final data = await Clipboard.getData('text/plain');
setState(() {
_copiedText = data?.text ?? 'データなし';
});
},
child: const Text('ペースト')),
FilledButton(
onPressed: () async {
final clipboard = SystemClipboard.instance;
if (clipboard == null) {
return;
}
final reader = await clipboard.read();
if (reader.canProvide(Formats.htmlText)) {
final html = await reader.readValue(Formats.htmlText);
setState(() {
if (html == null) {
_copiedText = 'データなし';
} else {
_copiedText = html;
}
});
}
},
child: const Text('ペースト')),
Text('コピーされたテキスト: $_copiedText'),
],
),
),
);
}
}
-
Next
記事がありません