🚀
[Flutter] swiperをPageViewの入れ子にした時、スクロールできない問題
swiperで何
flutter_swiperはflutterに一番使われたswiper plugin,ただ作者が全然更新しないので、今回他の人がメンテしてくれるcard_swiperを使う、使い方は同じです。
発生する問題
Flutterでは、PageViewがSwiperにネストされている場合、デフォルトでは問題なく動作し、内側のSwiperが先にスクロールイベントを消費する。
ただし、Swiperのレイアウトをカスタム:SwiperLayout.CUSTOMに設定した場合、Swiperはスクロールイベントを消費できない。
原因:
この問題は、PageViewとSwiperのスクロールイベントの競合により発生します。
解決策
解決策は二つある。
- もしインタラクションが許容されるなら、最も簡単な方法は、外側のPageViewのスクロールを単に無効にすることです。PageViewのphysicsをNeverScrollableScrollPhysicsに変更するだけです。
SwiperをListenerでラップして、onPointerDown、onPointerUpを監視して、タップされたタイミングでPageViewのphysicsをNeverScrollableScrollPhysicsに変更、指が離せたらPageViewのphysicsを戻す。
ここではRiverpodつかている、他の状態管理でも同じことすれば、実現できるはずです。StreamBuilderもできる。
Consumer(builder: (context, ref, _) {
return Listener(
onPointerDown: (_) {
ref.watch(onTapProvider.notifier).update((state) => true);
},
onPointerUp: (_) {
ref.watch(onTapProvider.notifier).update((state) => false);
},
child: Swiper(
layout: SwiperLayout.CUSTOM,
customLayoutOption:
CustomLayoutOption(startIndex: -1, stateCount: 3)
..addRotate([-45.0 / 180, 0.0, 45.0 / 180])
..addTranslate(const [
Offset(-370.0, -40.0),
Offset(0.0, 0.0),
Offset(370.0, -40.0)
]),
itemWidth: 300.0,
itemHeight: 200.0,
outer: false,
itemBuilder: (context, index) {
return Image.asset(
images[index],
fit: BoxFit.fill,
);
},
itemCount: images.length,
),
);
}),
- もう一つの方法は、Swiperの内部コードを変更することです。 flutterのpluginのコードを変更するのは非常に簡単で、コードをコピーアウトして修正するだけです。
具体的な変更箇所は、custom_layout.dart内の_buildAnimationメソッドで、以下のように変更する。
onPanStart、onPanEnd、onPanUpdateを
onHorizontalDragStart, onHorizontalDragEnd, onHorizontalDragUpdateにする。
return GestureDetector(
behavior: HitTestBehavior.opaque,
onPanStart: _onPanStart,
onPanEnd: _onPanEnd,
onPanUpdate: _onPanUpdate,
child: ClipRect(
child: Center(
child: _buildContainer(list),
),
),
);
↓
return GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragStart: _onPanStart,
onHorizontalDragEnd: _onPanEnd,
onHorizontalDragUpdate: _onPanUpdate,
child: ClipRect(
child: Center(
child: _buildContainer(list),
),
),
);
この問題が発生する原因はonHorizontalDragxxx
の優先順位 > onPanxxx
、これによりPageViewのonHorizontalDragxxx
が先に呼ばれる、SwiperのonPanxxx
が呼ばれなくなる。
タッチイベントの詳細はこちらに参考
参考code:
Discussion