FlutterのFocusableActionDetector
を使ってショートカットキーを設定したい!
概要
今回の記事では、FlutterのFocusableActionDetector
を使ってショートカットキーを設定する手順を掲載する。
仕様書
環境
- Android Studio Giraffe | 2023.2.1 Patch 2
- Flutter 3.19.6
- file_picker 8.0.3
手順書
インストール編とコード編の2部構成です。
インストール編
ファイルの入出力にfile_picker
を使ってるのでインストールする。
ターミナルでコマンドを実行するか
flutter pub add file_picker
pubspec.yaml
のdependencies:
に下記のような感じで追加して
dependencies:
file_picker: ^8.0.3
ターミナルでflutter pub get
する。
flutter pub get
コード編
下記のテキストファイルの入出力の例にショートカットキーを設定する。
Crtl
+O
でファイルを開き、Ctrl
+S
でファイルを保存する例。
import 'dart:io';
import 'package:file_picker/file_picker.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();
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() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['txt'],
);
if (result != null) {
File file = File(result.files.single.path!);
if (await file.exists()) {
final data = await file.readAsString();
textEditingController.text = data;
}
}
}
@override
Widget build(BuildContext context) {
return 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,
),
),
]),
),
),
);
}
}
FocusableActionDetector
にショートカットキーと紐づける動作を設定する。
複数のショートカットキーを設定するには例のようにshortcuts
にオブジェクトとして
shortcuts: <ShortcutActivator, Intent>{
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS):
SaveTextIntent(),
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyO):
OpenTextIntent(),
動作を設定するactions
も同様。
actions: {
SaveTextIntent: CallbackAction<SaveTextIntent>(
onInvoke: (intent) => saveText(),
),
OpenTextIntent: CallbackAction<OpenTextIntent>(
onInvoke: (intent) => loadText(),
),
トップレベルに動作に対応するIntent
を作っておく。
class SaveTextIntent extends Intent {}
class OpenTextIntent extends Intent {}
FocusableActionDetector
にフォーカスがあたってないとショートカットキーが反応しないのでautofocus: true
として自動的にフォーカスされるようにしておく。この設定で子のTextField
にフォーカスがあたらないということはない。
まとめ(感想文)
ショートカットキーがあることでアプリでの作業効率が上がるかもね!