torikatsu.dev

Flutterとかプログラミングとかガジェットとか書きます

Blocパターンでのフォームバリデーション

最近、Flutterでアプリ開発をしているのですが、Blocパターンでフォームのバリデーションのスマートな実装方法についてまとめられた記事があまりなく、どハマりしました。

そこで今回の記事では、BlocパターンでRxDartを用いたスマートなフォームのバリデーションの実装方法をサインインページを例として紹介したいと思います。

記事の対象:

Rx・flutter初心者

得られる情報

Blocを使ったフォームの実装方法

サインインページの要件は以下の通りとします。

  • メールアドレス

    @を含む1文字以上の文字列

  • パスワード

    8文字以上の英数字からなる文字列

はてなシンタックスハイライトが弱いので、今回はあえてスクリーンショットを用いてコードを掲載します。

1. UIを用意する

まずUIを用意します。今回はパスワードとメールの入力欄+送信ボタンのシンプルな構成とします。

UI
用意したUI

2. Blocを用意する

今回は以下のようなBlocクラスを用意します。

ポイントはBehaviorSubjectを使うところです。 BehaviorSubjectはイベントをキャッシュしておいてくれるので、過去のイベントに対し、BehaviorSubject.valueでアクセスできます。 

そのため入力情報を保持しておくフィールドを用意しなくてすみます。

Blocクラス
Bloc

Blocでは全ての入出力をStreamにするべきだとありますが、ここではあえてそのルールを破りsubmitをパブリックなメソッドとして定義しています。

なぜならば、ストリームでsubmitを定義すると送信ボタンのonPressedが冗長になってしまうからです。

ストリームで実装すると以下のように冗長な書き方になりますが、

onPressed: () => bloc.submit(null)

メソッドで定義しておくと

onPressed: bloc.submit

と定義でき、UI側のコードがすっきりします。

UIにBlocを注入する

Providerを使ってUIにBlocを注入します。
ついでにTextFieldとRaisedButtonにコールバックを渡します。

BlocをUIに注入
BlocをUIに注入

Providerはウィジェットの生成と破棄に合わせた処理を渡しておけるので便利です。

ここでProviderの直下にBuilderを配置していますが、これはこのクラス内からProvider.of(context)Blocにアクセスするためです。

なぜBuilderを挟む必要があるのかについてはまたいずれ記事にしたいと思います。

バリデーションの実装

いよいよバリデーションを実装していきます。 UI側にバリデーションの結果を伝えるためにStream.addErrorRx.combineStreamを使います

以下はバリデーションを追加したBlocクラスです。

バリデーション追加後のBloc
バリデーション追加後のBloc

ポイントはRx.combineLatestを使用することです。 これを使わずに送信ボタンにバリデーションの結果を通知するストリームを用意してもいいのですが、新しくストリームを生成しなければならないうえ、closeもしなくてはなりません。

今回のケースでは気になりませんが、Streamの数が増えるほどStreamの管理がしんどくなってくるので、combineLatestを使った方がスマートだと思います。

UIでバリデーションの結果を受け取る

UIでバリデーションの結果を受け取るにはInputDecorationクラスのerrorTextを利用します。

StreamBuilderで入力情報のストリームをリッスンしておき、エラーが流れてきたらsnapshot.errorがエラーテキストとして表示されると言った感じです。

また送信ボタンもバリデーションの結果を受け取れるようにしておきます。

UIでバリデーションの結果を受け取る
UIでバリデーションの結果を受け取る

コード全体像

コードの全体像は以下の通りです。 UI

UI全体
UI全体
Bloc全体
UI全体

おわりに

今回フォームの実装方法を探る中で、Rxdartのコードを読みました。英語が苦手で過ごし面食らいましたが、じっくり読んでみるとかなり丁寧にコメントがされていて、ネットの情報見るよりわかりやすかったので、もし困ったらコードのコメントを読むのをお勧めします。