お前らのFormのサンプルコードはすべて間違っている

# モチベーション
* 長らくFlutterにおける標準WidgetのFormとお付き合いをしていたところ、どうみても巷に蔓延っているFormに関わるサンプルコードは「想定していない使い方」だと確信したので、ここに記す。
* いうて[公式のサンプルコード](https://api.flutter.dev/flutter/widgets/Form-class.html)もだいぶ**ひどい**。各フォーム値の取得方法に関して何も書いてないし...

## コードの全容

<p class="codepen" data-height="480" data-theme-id="light" data-default-tab="js,result" data-user="hummer98" data-slug-hash="PoGjeaN" style="height: 480px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Flutter Form Demo">
  <span>See the Pen <a href="https://codepen.io/hummer98/pen/PoGjeaN">
  Flutter Form Demo</a> by hummer (<a href="https://codepen.io/hummer98">@hummer98</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

* [GitHubリポジトリ](https://github.com/hummer98/flutter_form_sample)

## ポイントその1: 意味もなくsave()するな
* 前の記事にも書いたが、`_formKey.currentState.save()`は、Formの子孫にあるFormFieldの`onSave()`を呼び出しているだけ。
* onSaveを書いてないなら何の意味もない行為である。
* `_formKey.currentState.validate()`のあとの***おまじない***やめよ？

## ポイントその2: 意味もなくステートを持つな
* フォームで使う値を一生懸命`var String email`とかやって保持している書き方が多いが、`FormFieldState`に`GlobalKey`張れば、フォームの現在値にはアクセスできる。
* フォーム値の出し入れ等が特殊であるようなカスタムFormFieldを使っているのでも無いかぎり、わざわざ`StatefulWidget`にして値を保持し`setState()`をして現在のフォームに入力されている値を保持しようとするのは意味がない。
  * フィールドが10個とかだと, さすがに`GlobalKey`を作りまくって出し入れするのがダルいので、素直に[reactive_forms](https://github.com/joanpablo/reactive_forms)を使ったほうが早い。とても良いフォームライブラリです。([AutofillHints対応](https://github.com/joanpablo/reactive_forms/issues/41)あくして→自分でPRしてどうぞ？）

## ポイントその3: やたらめったらFormを書き換えるな
* `FormField`系はわりと重いWidgetなので、バカスカ書き換えるのはやめたほうがいい。
* そもそも`StatefulWidget`なので、入力値が変われば勝手に書き換わるし、バリデーションがコケれば発生すれば勝手にrebuildしてエラーメッセージを表示する
  * 上記コードを動かしてみればLoginFormのrebuildが最初の1回だけで、あとはエラーが出ようが入力されようが一切rebuildされてないのがわかるはず
* 外からバカスカsetStateして書き換える必要は皆無（大事なことなので２度

## ポイントその4: 初期値を入れるのにTextEditingControllerを使うな
* 初期値を入れるには`TextEditingController`を使えとか書いてあるblogがあるが、意味不明である。
* その名もずばり`TextFormField.initialValue`があるのでそれを使えば良い。

## ポイントその5: AutofillHintsちゃんと書こう
* 各OSにテキストフィールドの入力支援をさせるために、`TextFormField.autofillHints`（HTML.Inputタグで言うところの[autocomplete属性](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/autocomplete)）をセットしておこう
* これをやっておくだけでユーザー体験が劇的に向上する


# 元凶と経緯
* FormWidgetは元来`_fields`というchild以下の各フォームのアクセサを持っているのにもかかわらず、この配列が`private`になっていて、**ユーザから触れない**
* そのため、FormFieldそれぞれに`GlobalKey<FormFieldState>`を張っていかないと、フォームの現在値にアクセスすることができない。
* Formに関する公式のドキュメントを眺めていても、肝心のフォームの現在値にアクセスする手段がわからない（公式にもちゃんと書いてない）ので、思い思いの実装によって解決するBlogが蔓延してしまったのではないだろうか。
* →***なにもかも公式が悪い***

# おまけ

## 外からフォームの値を書き換えたいとき
* `FormFieldState<T>.didChange(T value)`([公式doc](https://api.flutter.dev/flutter/widgets/FormFieldState/didChange.html))を使う。
  * `_fieldKey.currentState.didChange(newValue);`みたいな感じ
* ちなみに`setValue`([公式doc](https://api.flutter.dev/flutter/widgets/FormFieldState/setValue.html))は`protected`なので、カスタムしたいFormFieldを継承して作成するときに使う模様。

## EnterキーでSubmitしたいとき
* WebFormだとよくある動作
* ちゃんとやるとキーボードイベントを取得して判定するべきなんだけど、雑にやるなら`onFieldSubmitted`にボタンのsubmitと同じ処理を呼び出すようにしておくと、同じような感じに動く。

# まとめ
* 標準のFormはめっちゃ使いにくいので[ReactiveForm](https://github.com/joanpablo/reactive_forms)使いましょう！

# Follow Me!
* Twitter: [hummer](https://twitter.com/hummer)