Flutterでキーボードショートカット【初級編】
Flutterでキーボードショートカットを使うには、いくつか方法がある。
今回は、Focus
とonKeyEvent
を使って、Widgetを開き1つのキーが押されたときの処理を実装する。
(Widgetを開かない通常画面でショートカットを使ったり、複数キーを使ったショートカットなど、より複雑なキー入力処理については、今後【中級編】【上級編】という形で扱うかも?)
正直よくわかってないが、一応動作したのでまとめておく。
今回の記事でできること
開いたWidgetで1つのキーが押されたときの処理ができる。
以下の六法アプリでは、右下のプラスボタンから法令検索し法令を追加した後、条文が表示されているエリアをダブルクリックすると条番号検索するモーダルが現れる。
このモーダルで、表示されているボタンを使わずに、数字キーで数字を入力、BackSpaceキーで数字を一つ削除、EnterまたはSpaceキーで条番号へジャンプできる。
また、Sキーを押してモーダルを閉じることもできる。
この記事ではこうしたキー入力処理を実現する。
Flutterにおけるキーボードショートカットの取り扱い
公式ページは以下のリンク。
※Focus状態は、TextFieldとかであるautofocusをイメージするとわかりやすいかも。autofocusを指定すると、TextFieldが表示されたときに、TextFieldで入力が受け付けられる状態になるが、この入力が受け付けられる状態というのがまさにFocus状態。
使い方
Focus Widget
今回の記事では、既述の通り、Focus
とonKeyEvent
を使って、1つのキーが押されたときの処理を実装する。
Focus
クラスについての公式ページ↓
Focus
を使ったコードは以下のような感じ。
Focus(
autofocus: true,
onKeyEvent: (node, event) {},
child: Widget()
)
まず、キーボードショートカットを使いたいWidgetを包むFocus
を作る。
そして、Widgetが表示されたときにFocus状態になるようにautofocusをtrueにする。
キー入力の処理は、onKeyEventで行う。
onKeyEvent
onKeyEventは以下のような感じ。
onKeyEvent: (node, event) {
if (event is KeyDownEvent) {
if (event.logicalKey == LogicalKeyboardKey.keyQ) {
//処理
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
}
if (event is KeyDownEvent) {}
というif文は、キーが押し下げられたときにのみ処理するためのもの。これがないと、キーが押し下げられたときとキーが戻ってくるときの2回処理がされてしまう。
そして、if (event.logicalKey == LogicalKeyboardKey.keyQ) {}
で、Qキーが押されたときの処理を行っている。
ここではlogicalKeyを使っているが、physicalKeyというのもある。
logicalKeyは実際のキー、physicalKeyはQWERTYキーボードでの特定の位置のキーを指す。
キーの印字通りにするならlogicalKeyでOK。
使えるキーについては以下の公式ドキュメントを参照。
スペースバーはLogicalKeyboardKey.space
、エンターキーはLogicalKeyboardKey.enter
みたいに使える。
また、onKeyEventでは、返り値としてKeyEventResultが必要。正直よくわかってないが、Focus
の公式ドキュメント通り、keyが押された処理のif文ではKeyEventResult.handled
を、onKeyEventの最後のところにはKeyEventResult.ignored
をreturnしている。
使用例
この記事の最初に紹介した六法アプリでの使用例。
キーを押したときの処理などは省略している。
//
String s = '';
//
Focus(
autofocus: true,
onKeyEvent: (node, event) {
final key = event.logicalKey;
final keyl = key.keyLabel;
if (event is KeyDownEvent) {
if (key == LogicalKeyboardKey.space ||
key == LogicalKeyboardKey.enter ||
key == LogicalKeyboardKey.numpadEnter) {
//スペース、エンター、テンキーのエンターが押されたときの処理
return KeyEventResult.handled;
} else if (key == LogicalKeyboardKey.backspace) {
//バックスペースが押されたときの処理
return KeyEventResult.handled;
} else if (keyl.contains(RegExp(r'[0-9]'))) {
//数字キー、テンキーの数字キーが押されたときの処理
setState(() {
s += keyl[keyl.length - 1];
});
return KeyEventResult.handled;
} else if (key == LogicalKeyboardKey.keyS) {
//Sキーが押されたときの処理
Navigator.of(context).pop();
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
},
child: //
)
//
まず、このFocusはshowModalBottomSheetのbuilder > BottomSheet > StatefulBuilder内に置き、Focusのchildにはボタンとかを包んだContainerを指定している。
そして、onKeyEventのはじめのとこで、event.logicalKey
とkey.keyLabel
をそれぞれkey
, keyl
として、コードを若干短縮している。
また、通常のキーとテンキーのキーは別キーとして扱われるため、テンキーで使うにはLogicalKeyboardKey.numpadEnter
みたいな感じで指定が必要。
数字キーも同様に、通常はdigit0
とかで指定できるが、テンキーではnumpad0
みたいな指定になる。
今回の使用では、押された数字キーの数字を画面に反映させたかったため、keyLabel
を使った処理にした。
keyLabelを使った数字キーの処理について
まず、if文の条件でkeyl.contains(RegExp(r'[0-9]'))
としている。
これは、keyLabelに0~9の数字が含まれているかどうかを判定している。
keyLabelはキーの名前として設定されている値で、onKeyEvent内ではevent.logicalKey.keyLabel
として取得できる。
通常の数字キーでのkeyLabelはそのままの数字として取得できる(0とか)が、テンキーの数字キーでのkeylabelはNumpad 0
みたいにNumpadがついてしまうため、0~9が含まれるかどうかという条件で判定している。
同様の理由で、押されたキーの数字を取得するためにkeyl[keyl.length - 1]
を使用した。
これは、keyLabelの最後の文字を取得するもの。
テンキーでのkeyLabelがNumpad 0
という感じなので、最後の文字を取得すればキーの数字を取得できる。
問題として、数字が含まれるファンクションキーにも反応してしまうという点があるが、ファンクションキーはこのアプリで使用しないのでよしとした。ファンクションキーに反応しないようにするには、keyLabelにFが含まれる場合を除くとかすればいいかも?
まとめ
ということで、Focus
とonKeyEvent
を使って、Widgetを開き1つのキーが押されたときの処理のやり方をまとめた。
次回の中級編?では、ModalとかのWidgetを開かずに(最初の画面とか)でショートカットを使ったり、TextFieldなど他のfocusがあるところでショートカットを使う方法をまとめたい。(ショートカットの使い方というよりもFocusNodeの説明になるかも)
中級編の記事の内容によって、以下の六法アプリで使ったショートカット機能がすべて実装できる予定です。
(以下の六法アプリでは、Cを押すとショートカットを確認できます。)
Discussion