Flutterのキーボードで閉じるボタンを表示する

# 閉じるボタンがほしい

iOSアプリで複数行テキスト入力とかさせようとすると、入力フィールドにフォーカス来た時に表示されるキーボードをどうやって閉じるかが問題になる。この解決策としてよくやるのが、

- 他の部分タップしたらフォーカス外す。
- キーボードの上に「閉じる」ボタンを追加する。

前者で良ければ話は終わるが、後者しか手段がない場合にどうするか。iOSネイティブならinputAccessoryViewを使えば良い。Flutterでも同じ機能あれば良い…が、探してもなかった。Androidではあまり問題にならないから仕方ないのかも。
でもきっと解決している方がいるに違いない。ググったけどあまりないなぁ…と思いながら見つけたのがkeyboard_actions。

- [keyboard_actions](https://pub.dev/packages/keyboard_actions) (pub.dev)
- [Keyboard Actions](https://github.com/diegoveloper/flutter_keyboard_actions) (GitHub)

# 閉じるボタンがほしいだけ

keyboard_actionsはネイティブに頼らずFlutter/Dartだけで解決していて素晴らしいし、いろいろ機能あるしで感謝感謝なのだが、俺がほしいのは単に「閉じる」ボタンだけなのだ。keyboard_actionsを使ってみると分かるがほどよいタイミングで設定を渡したりしなければならず、もうちょっと楽に書きたい。

なのでこんなコード書いた。

```dart:keyboard_actions_helper.dart
import 'package:flutter/material.dart';
import 'package:keyboard_actions/keyboard_actions.dart';

class KeyboardActionsHelper {
  static const defaultCloseWidget =
      Padding(padding: EdgeInsets.all(5.0), child: Text('閉じる'));

  static void setCloseActions({
    @required BuildContext context,
    @required List<FocusNode> focusNodes,
  }) {
    final actions = focusToCloseActions(closeActionFocusNodes: focusNodes);
    final config = actionsToKeyboardBarConfig(actions: actions);
    FormKeyboardActions.setKeyboardActions(context, config);
  }

  static List<KeyboardAction> focusToCloseActions(
      {@required @required List<FocusNode> closeActionFocusNodes}) {
    return closeActionFocusNodes
        .map<KeyboardAction>((focus) =>
            KeyboardAction(focusNode: focus, closeWidget: defaultCloseWidget))
        .toList();
  }

  static KeyboardActionsConfig actionsToKeyboardBarConfig(
      {@required List<KeyboardAction> actions}) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: false,
      actions: actions,
    );
  }
}

class ConfigurableFormKeyboardActions extends FormKeyboardActions {
  final List<FocusNode> closeActionFocusNodes;

  ConfigurableFormKeyboardActions(
      {this.closeActionFocusNodes, Widget child, bool autoScroll = true})
      : super(child: child, autoScroll: autoScroll);

  @override
  FormKeyboardActionState createState() => ConfigurableFormKeyboardActionState(
      closeActionFocusNodes: closeActionFocusNodes);
}

class ConfigurableFormKeyboardActionState extends FormKeyboardActionState {
  final List<FocusNode> closeActionFocusNodes;

  ConfigurableFormKeyboardActionState({this.closeActionFocusNodes});

  @override
  void initState() {
    super.initState();
    _setupActions();
  }

  void _setupActions() {
    if (closeActionFocusNodes != null) {
      setConfig(
        KeyboardActionsHelper.actionsToKeyboardBarConfig(
          actions: KeyboardActionsHelper.focusToCloseActions(
              closeActionFocusNodes: closeActionFocusNodes),
        ),
      );
    }
  }
}
```

するとこんな感じでFocusNode渡すだけで済む。

```dart:foo.dart

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ...
      body: ConfigurableFormKeyboardActions(
        closeActionFocusNodes: [_yourFocusNode],
        child: //...

```

ただそれだけの話であるが、keyboard_actions や日頃お世話になっている皆様への感謝として晒しておきます。