非同期関数の処理結果を画面に表示するときに使用するFutureBuilderだけど非同期関数の処理時間が極端に短い場合に、ちょっと個人的にどうかなと思うところがあった。
その内容と対策などについて考えてみた。
FutureBuilderと問題点
FutureとAsyncSnapshotの関係
与えられたFutureによりAsyncSnapshotの状態を変更してbuilderが起動される。
FutureBuilderで使われるAsyncSnapshotの状態は3つ。
- ConnectionState.none
Futureが呼び出される直前に設定される。 - CectionState.waiting
Futureが呼び出された後に設定される。
ステータスのみの変更なので、initialDataがある場合はhasDataという状態でもある。 - ConnectionState.done
Futureが完了した時に設定される。
正常終了の場合はFutureのリターン結果がdataに設定される。
エラーとなった場合は、errorにエラー情報が設定される。
表示上の問題
実装の都合上かもしれないが、どんなにFutureの処理が早くてもFutureBuilderのbuilder呼び出しは2回行われる。
waiting→doneという順番。
これに対応する処理方法としては、基本的にFlutter APIのヘルプに準じた実装になると思われる。
上記ページの例を元に実装すると、以下のような画面表示になる。
データが確定されていないwaiting時にインジケーターを表示するという形式。
問題となるのが、Futureの処理が速い場合。
上のサンプルは300ミリ秒の遅延を入れてるので、ちょっとだけインジケーターの表示が見えるのだけど、その時間がもっと短い場合、人の目ではインジケーター表示が確認できずパッとリストがフラッシュ表示されるような状態になってしまう。
このフラッシュ表示になってしまうというのが、ここでの問題点。
対策1
waiting時にインジケーターを表示しないようにするというのが一つ目。
これを実現するために、初期データを与え常にデータがあるという状態にしておく。
これを実装して表示したのが以下のもの。
左が従来通りwaiting時にインジケーターを表示し、右側は初期状態でからのデータをinitialDataに与えておいた場合。builder内の処理としてはAsyncSnapshotのデータだけ見てListViewの生成をしている。
こちらもデータが生成するまで300ミリ秒の遅延を入れてあるのだけど、一応よさげな表示になる。
ただ、計算完了までが短いということが分かっていればこの方法でも問題はないのだろうけど、計算完了までの時間が不明の場合、特に遅くなった場合以下の様な感じになってしまう。
初期表示時に何も表示されていない状態が続き、止まってしまっているのではないかという印象を持たれる画面になってしまう。
対策2
Futureの処理が速い場合はインジケーターを表示せず、遅い場合はインジケーターを表示するという方法ができないか考え、当初はStatefulWidgetでFutureの状態を監視してインジケーターを表示するタイミングを決定させようと思い、その実装を行った。
後、現在のFutureBuilderを改訂し使われていないAsyncSnapshotの状態activeを使えないか考え、次のようなウィジェットを作成してみた。
追加したパラメータはDuration? activeTime
で、ここで設定した時間だけwaitingの前にactiveの状態があるようにしてみた。
そのサンプルが下のもの。対策2のウィジェットを使ったのが右側で、activeTimeに300ミリ秒を使用したものなのだが、その遅延時間以上にFutureの処理が遅ければインジケーターを表示するようにしている。
activeTimeの設定がない場合はFutureBuilderと同じ動きになる。
コメント