Flutterのパッケージdesktop_drop
を使ってアプリのウィンドウにファイルをドラッグアンドドロップしたい!
概要
今回の記事では、Flutterのパッケージdesktop_drop
を使ってアプリのウィンドウにファイルをドラッグアンドドロップする手順を掲載する。
仕様書
環境
- Android Studio Giraffe | 2023.2.1 Patch 2
- Flutter 3.19.6
- desktop_drop 0.4.4
手順書
インストール編とコード編の2部構成です。
インストール編
パッケージdesktop_drop
と併せてパッケージcross_file
も必要になるので一緒にインストールする。
ターミナルでコマンドを実行するか
flutter pub add desktop_drop cross_file
pubspec.yaml
のdependencies:
に下記のような感じで追加して
dependencies:
desktop_drop: ^0.4.4
cross_file: ^0.3.4+1
ターミナルでflutter pub get
する。
flutter pub get
コード編
下記の過去の記事に載せたテキストファイルの入出力する例に
ファイルをウィンドウにドラッグアンドドロップすると最初のファイルの内容をTextField
に表示するようにした例。
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:file_picker/file_picker.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: const FilePickerSample1()
)
)
);
}
class SaveTextIntent extends Intent {}
class OpenTextIntent extends Intent {}
class FilePickerSample1 extends StatelessWidget {
FilePickerSample1({super.key});
TextEditingController textEditingController = TextEditingController();
List<XFile> droppedFiles = [];
bool dragging = false;
Offset? offset;
void saveText() async {
String? outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Please select an text file:',
fileName: 'output.txt',
);
if (outputFile == null) {
return;
}
final file = File(outputFile);
await file.create();
await file.writeAsString(textEditingController.text);
}
void loadText({String filePath = ""}) async {
FilePickerResult? result;
if (filePath == "") {
result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['txt'],
);
if (result != null) {
filePath = result.files.single.path!;
} else {
return;
}
}
File file = File(filePath);
if (await file.exists()) {
final data = await file.readAsString();
textEditingController.text = data;
}
}
@override
Widget build(BuildContext context) {
return DropTarget(
onDragDone: (detail) async {
droppedFiles.addAll(detail.files);
debugPrint('onDragDone:');
if (detail.files.isNotEmpty) {
loadText(filePath: detail.files[0].path);
}
// Debug用
for (final file in detail.files) {
debugPrint('${file.path} ${file.name}'
' ${await file.lastModified()}'
' ${await file.length()}'
' ${file.mimeType}');
}
},
onDragUpdated: (details) {
offset = details.localPosition;
debugPrint('x:${offset!.dx}, y:${offset!.dy}');
},
onDragEntered: (detail) {
dragging = true;
offset = detail.localPosition;
debugPrint('x:${offset!.dx}, y:${offset!.dy}');
},
onDragExited: (detail) {
dragging = false;
offset = null;
},
child: FocusableActionDetector(
autofocus: true,
shortcuts: <ShortcutActivator, Intent>{
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS):
SaveTextIntent(),
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyO):
OpenTextIntent(),
},
actions: {
SaveTextIntent: CallbackAction<SaveTextIntent>(
onInvoke: (intent) => saveText(),
),
OpenTextIntent: CallbackAction<OpenTextIntent>(
onInvoke: (intent) => loadText(),
),
},
child: SafeArea(
child: Center(
child: Column(children: [
Row(children: [
IconButton(
icon: const Icon(Icons.upload),
onPressed: () {
loadText();
},
),
IconButton(
icon: const Icon(Icons.download),
onPressed: () {
saveText();
},
),
]),
const SizedBox(height: 8),
Expanded(
//width: double.infinity,
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'テキスト',
floatingLabelBehavior: FloatingLabelBehavior.always,
),
controller: textEditingController,
maxLines: null,
expands: true,
textAlignVertical: TextAlignVertical.top,
),
),
]),
),
),
),
);
}
}
複数のファイルをドラッグアンドドロップした場合もTextField
には先頭のファイルの内容のみ表示されるんだけども、デバッグのためにドラッグアンドドロップされたファイルの情報を全てコンソールに出力してる。
localPosition
はファイルがドロップされたウィンドウの座標。ウィンドウの左上が0
。
まとめ(感想文)
それっぽいデスクトップアプリの動作になってきた感じ。
引用・参考文献
下記の記事を参考にさせていただきました。ありがとうございました。