このページには広告が含まれる場合があります。
これは、ListViewとFutureBuilderを組み合わせたFlutterアプリに起こるトラップ的な話なのです。
以下のgifのような感じで、リストの一番にしたに来た状態から少しだけスクロールアップさせようとすると、なんとリストの一番先頭(ないしはだいぶ上の方)まで戻ってしまうという現象が発生してしまいます。
この発生原因と対処法のご案内です。
原因
原因は、このstackOverFlowの記事にもありますが、

I am creating a news feed for my app and as I scroll down data is fetched from Firestore. As soon as I scroll up the listview literally snaps to the very top skipping all the other items in between...
- ListView・ScrollViewは現在画面に見えていない要素(children)は一旦破棄(dispose)されて、最後画面に表示される段階になった際に、再度インスタンスが生成される(描画コスト抑制のため・AndroidのRecyclerViewと同じ発想)。
- しかし、今回の場合は要素(children)をFutureBuilderで生成しているため、インスタンスが再生成されるまでにタイムラグが生じる。
- そのため、スクロールアップして、画面に消えていた要素(children)を再描画するために、その要素のインスタンスを再生成する際に、FutureBuilderが起動時に、データ取得までの一瞬のタイムラグの間にProgressIndicatorが描画されることで、スクロール前のページの状態がクリアされてしまい、リスト全体がイチから再描画されてしまったため、リストの先頭に戻ってしまった。
ということのようです。
対処法
ですので、対処法としては、
- 「状態を保持したまま画面の切り替え(再描画)」をするために、「AutomaticKeepAliveClientMixin」を使う
- AutomaticKeepAliveClientMixinはStateクラスを継承したクラスと一緒に使う仕様になっているために、StatefulWidgetに
- wantKeepAlive(現在の状態を保持するかどうかを格納するgetterプロパティ)は、AutomaticKeepAliveClientMixinが実装しないといけないものなので必要(これをtrueにすることで状態を保持できる)
で解決できるようです。
A mixin with convenience methods for clients of AutomaticKeepAlive. Used with State subclasses.
Subclasses must implement wantKeepAlive, and their build methods must call
super.build
(the return value will always return null, and should be ignored).
(詳しくは、下の動画で解説していますので、よろしければ御覧ください)