Refresh Indicator が動かない
YUMEMI Flutter Advent Calendar 2023 12日目の記事です
みんな大好きRefreshIndicator(諸説あります)
Refresh Indicator を使ったことのないFlutterエンジニアはあまり居ないと思います。
同様に、なぜか Refresh Indicator が動作しない事象に遭遇した方もいらっしゃるのではないでしょうか?
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
...List.generate(
25,
(index) => Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Text('item $index'),
),
),
],
),
);
検索してみると、AlwaysScrollableScrollPhysics
を設定するといった解決法をよく見かけます
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(children: [...])
)
確かに AlwaysScrollableScrollPhysics
を設定することで、 RefreshIndicator
が動作するようになります
しかしなぜ AlwaysScrollableScrollPhysics
を設定すると動作するようになるのでしょうか?
深堀してみましょう
Dive in to RefreshIndicator
RefreshIndicator
の実装を見てみると、いくつかの条件を満たした場合にのみ、 RefreshIndicator
を開始するとあります
条件のひとつにドラッグ中であるかどうか( dragDetails
が null でないかどうか)があることがわかります
そういえば先ほど RefreshIndicator
が動作しないケースは、スクロールが不要なリスト長でした
試しにリストの要素数を増やしてみましょう
RefreshIndicator
が動作するようになりましたね!
dragDetails が必要
以下箇所で受け取った ScrollNotification
が、 dragDetails
を持っているという条件が、 RefreshIndicator
を表示する一つの条件であることがわかりました
次に dragDetails
がどこで設定されているか追ってみましょう
先ほど提示した条件式を改めてみてみると、 ScrollNotification
が ScrollStartNotification
であることも一つの条件でした
ScrollStartNotification
が dispatch されるのは DragScrollActivity#dispatchScrollStartNotification
が呼ばれた場合のようです
DragScrollActivity が必要
では ScrollActivity
に DragScrollActivity
が設定されるのはどこなのかとコードを追っていくと、
最終的に Scrollable#setCanDrag
にたどり着きます
setCanDrag
に true を渡すとドラッグ可能となり、最終的に DragScrollActivity
が dispatch されるという仕組みのようです
では setCanDrag
に値を渡しているのはどこかと言うと、
それは ScrollPositionWithSingleContext
などであり、
ScrollPhysics#shouldAcceptUserOffset
が返す値であるわけです
ここで AlwaysScrollableScrollPhysics
の実装を見てみましょう
AlwaysScrollableScrollPhysics
を渡すことで RefreshIndicator
が動作していたのは、
AlwaysScrollableScrollPhysics#shouldAcceptUserOffset
が常に true を返しているから、ということがわかりましたね!
ここでネタバラシ
ここまで読んでくださった皆さまにひとつお詫びがあります
実は最初のコード、シンプルに CustomScrollView
+ SliverList
+ SliverChildBuilderDelegate
を使えばリスト長に依らず RefreshIndicator
が動作してくれます
もちろん内部で CustomScrollView
を使っている ListView.builder
でも問題ありません
ただしウィジェット構成によっては動作しないこともあり得ますし、フレームワークの実装を見てみることで、どういった条件で RefreshIndicator
が動作するのかどうかの理解を深めることは、ちょっとした財産になるかと思います
ぜひ RefreshIndicator
の内部実装に思いをはせながら、 swipe-to-refresh を楽しんでみてください
Discussion