【Flutter】キーイベントとキーボードショートカット(非同期処理含む)
概要
Flutterアプリ開発での「キーイベント」及び「キーボードショートカット」の実装(非同期処理含む)についての備忘録を記す。
まあ、事の始まりはテキスト入力後Enterでどんどん追記出来るようなメモアプリを作りたく思い📝
●GitHub
今回は「main.dart」のみと言うこともあり、GitHubには上げてません。
※ソースを直接コピペで動作出来ます。
●参考になったサイト
実装
検証環境
※ 基本的なFlutter開発環境は整っている事を前提とします
プロジェクトを作成
●新規プロジェクトの作成
1.コマンドパレット(Command + Shift + P)を開き「flutter」と入力し、「Flutter: New Project」を選択。
2.一番上の「Application」を選択。
3.ワークスペースとなるディレクトリを選択(この中にプロジェクトが作成されます)
プロジェクト名は「note_flutter_keyevent」とします。
下準備
import 'package:flutter/services.dart';
今回は特にパッケージの導入は不要ですが、標準ライブラリの「services.dart」のインポートが必要となります。
土台を作成する
まずは土台を作成します。
キー操作関連は、何かのウィジェットに対し「フォーカス状態」にする必要があります。
今回は学習用サンプルなので、ダミーとしてTextを配置しておきましょう。
【main.dart】
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // これを追加
void main() {
final text = Text('テキスト');
final body = SafeArea( // ボディー
child: Column(
children: [
text,
],
)
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
相変わらずmain()直下にウィジェットを書きまくるという暴挙に出てますが、検証用なのでご容赦を🐣
キーイベント - 基本
●基本
対象ウィジェットを「フォーカス状態」にする必要がある。
これをする為、Focusウィジェットの中に対象ウィジェット(今回はText)入れて使います。
Enterキー押下時の例です
autofocus:自動フォーカスさせるため trueにします
onKeyEvent:キーイベント発生時のコールバック
if (event is KeyDownEvent) {}:キーイベントは「キーが下に下がった」「キーが戻し上がった」の2種類があるので分岐する(これをしないと同じ処理が2回されてしまう)
child:対象ウィジェットを指定
【注意点】
例えばTextField(テキスト入力欄)にすぐに入力出来るようにフォーカスする場合は
「TextFieldの方に autofocus: true 」を付けます。( ※ Focusの方には付けてはいけない )
【返り値について】
なお、Focusウィジェットですが「KeyEventResult」を返す必要があります。
・KeyEventResult.handled
ハンドルした場合に返す(今回はEnterキー押下時)
・KeyEventResult.ignored
特に何もしなかったキーの場合に返す
KeyEventResultをサジェストして見ると
ちなみに「KeyEventResult.skipRemainingHandlers」と言うのもあるらしい。
【コード】
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // これを追加
void main() {
final text = Text('テキスト');
final textFocus = Focus(
autofocus: true, // 自動フォーカス,
onKeyEvent: (node, event) {
final key = event.logicalKey;
if (event is KeyDownEvent) {
if ( key == LogicalKeyboardKey.enter ) { // Enterキー押下時
print('Enterキーが押されました');
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
},
child: text,
);
final body = SafeArea( // ボディー
child: Column(
children: [
textFocus,
],
)
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
・動作確認
Enterキーを押下
Enterキーを押下(2回目)
キーイベント - 非同期処理
●非同期処理の場合
例えば、こんな感じで非同期処理をしたいとする
await Future.delayed(Duration(seconds: 10));
print('10秒後に実行');
この為、onKeyEventに「async」指定しようとするも
『The argument type 'Future<KeyEventResult> Function(FocusNode, KeyEvent)' can't be assigned to the parameter type 'KeyEventResult Function(FocusNode, KeyEvent)?'.』
と言うエラーが発生。
そう、onKeyEventではasyncに対応していないのです。
(onPressedやonTapは対応しているのに ……)
これは困ったぞ。
そもそもEnterキーでメモ登録。つまりDB接続(非同期処理)をしたいと言うのに。
●解決策
色々調べてみた結果「Stack Overflow」に解決策が掲載されていた。
( ※ 概要 - 参考になったサイト にリンクを貼っておきました )
それを参考に改修してみる
非同期的な関数分割をして
こんな感じで呼び出せばオッケー🙆
非同期関数の呼び出しと言うことを分かりやすくする為 .then((value) { …… }) を付けたが別に省いても良い。
【コード】
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // これを追加
Future<KeyEventResult> manageKeyboard(KeyEvent event) async {
final key = event.logicalKey;
if (event is KeyDownEvent) {
if ( key == LogicalKeyboardKey.enter ) { // Enterキー押下時
await Future.delayed(Duration(seconds: 10));
print('10秒後に実行');
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
}
void main() {
final text = Text('テキスト');
final textFocus = Focus(
autofocus: true,
onKeyEvent: (node, event) {
manageKeyboard(event).then((value) {
print(value);
});
// 本来は「KeyEventResult.handled」との出し分けをするべきだが、今回は省略。
return KeyEventResult.ignored;
},
child: text,
);
final body = SafeArea( // ボディー
child: Column(
children: [
textFocus,
],
)
);
final sc = Scaffold(
body: body, // ボディー
);
final app = MaterialApp(home: sc);
runApp(app);
}
キーボードショートカット - 基本
キーボード関連(検知や操作など)のウィジェットを色々ありますが、今回「FocusableActionDetector」ウィジェットを使っていきたいと思います。
ウィジェット名から察するに
Focusable → フォーカス出来る
Action → アクション
Detector → 検出器
といった感じのウィジェットです。
shortcuts:ショートカット及びそれに紐づくインテントを設定。
actions:アクション。「インテント時の処理関数」を設定。
※ 「Control + N」押下時を例とします。
・インテントクラスを定義
・インテント時の処理関数を定義
・ショートカットキーの定義
LogicalKeySetオブジェクトを作成(「Control + N」の例)
【FocusableActionDetector】
autofocus:自動フォーカスさせるため trueにします
shortcuts:LogicalKeySetとそれの対応インテントを設定
actions:インテント時の処理関数を設定。
child:対象ウィジェットを指定(bodyに該当するものを丸ごと入れている例)
【コード】
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // これを追加
class TestIntent extends Intent {}
void _test() {
print('「Control + N」が押されました');
}
void main() {
final text = Text('テキスト');
final body = SafeArea( // ボディー
child: Column(
children: [
text,
],
)
);
// 「Control + N」
final lksControlN = LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyN);
final faDetector = FocusableActionDetector(
autofocus: true,
shortcuts: <ShortcutActivator, Intent>{
lksControlN:
TestIntent(),
},
actions: {
TestIntent: CallbackAction<TestIntent>(
onInvoke: (intent) => _test(),
),
},
child: body,
);
final sc = Scaffold(
body: faDetector, // FocusableActionDetector
);
final app = MaterialApp(home: sc);
runApp(app);
}
●動作確認
特に問題なくデバッグ文が出力される。
キーボードショートカット - 非同期処処理
非同期処理ですが、すんなりいきました。
↑これを
↓こう
これで、DB処理とかも問題無さげですね💡
【余談】note「見出し画像」でgifファイルは使えない
余談ですが、noteの「見出し画像」はCanvaで作成してます。
で、今回キーボードの背景を探していたのですが、Gif画像で動くものがあったんですよね。
これ、「見出し画像」で動いていたらかっこいいなと思ったのですが、いざnoteでやってみると「見出し画像でGifファイルは出来ない」です😑
まあ、記事の中で使う分には、使えるみたいですね。
上記の画像、あなたのブラウザでは動きがあるでしょうか❓
著書
【辛島信芳の著書】
IT技術などに興味のある方は、是非ご覧になってください。
この記事が気に入ったらサポートをしてみませんか?