83
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutter #2Advent Calendar 2020

Day 21

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

Last updated at Posted at 2020-12-20

モチベーション

  • 長らくFlutterにおける標準WidgetのFormとお付き合いをしていたところ、どうみても巷に蔓延っているFormに関わるサンプルコードは「想定していない使い方」だと確信したので、ここに記す。
  • いうて公式のサンプルコードもだいぶひどい。各フォーム値の取得方法に関して何も書いてないし...

コードの全容

See the Pen Flutter Form Demo by hummer (@hummer98) on CodePen.

ポイントその1: 意味もなくsave()するな

  • 前の記事にも書いたが、_formKey.currentState.save()は、Formの子孫にあるFormFieldのonSave()を呼び出しているだけ。
  • onSaveを書いてないなら何の意味もない行為である。
  • _formKey.currentState.validate()のあとのおまじないやめよ?

ポイントその2: 意味もなくステートを持つな

  • フォームで使う値を一生懸命var String emailとかやって保持している書き方が多いが、FormFieldStateGlobalKey張れば、フォームの現在値にはアクセスできる。
  • フォーム値の出し入れ等が特殊であるようなカスタムFormFieldを使っているのでも無いかぎり、わざわざStatefulWidgetにして値を保持しsetState()をして現在のフォームに入力されている値を保持しようとするのは意味がない。
    • フィールドが10個とかだと, さすがにGlobalKeyを作りまくって出し入れするのがダルいので、素直にreactive_formsを使ったほうが早い。とても良いフォームライブラリです。(AutofillHints対応あくして→自分でPRしてどうぞ?)

ポイントその3: やたらめったらFormを書き換えるな

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

ポイントその4: 初期値を入れるのにTextEditingControllerを使うな

  • 初期値を入れるにはTextEditingControllerを使えとか書いてあるblogがあるが、意味不明である。
  • その名もずばりTextFormField.initialValueがあるのでそれを使えば良い。

ポイントその5: AutofillHintsちゃんと書こう

  • 各OSにテキストフィールドの入力支援をさせるために、TextFormField.autofillHints(HTML.Inputタグで言うところのautocomplete属性)をセットしておこう
  • これをやっておくだけでユーザー体験が劇的に向上する

元凶と経緯

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

おまけ

外からフォームの値を書き換えたいとき

  • FormFieldState<T>.didChange(T value)(公式doc)を使う。
    • _fieldKey.currentState.didChange(newValue);みたいな感じ
  • ちなみにsetValue(公式doc)はprotectedなので、カスタムしたいFormFieldを継承して作成するときに使う模様。

EnterキーでSubmitしたいとき

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

まとめ

  • 標準のFormはめっちゃ使いにくいのでReactiveForm使いましょう!

Follow Me!

83
63
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
83
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?