業務でFlutterを用いる際、簡単にカルーセルを使用するためにcarousel_slider
というパッケージを導入しました
carousel_slider
の機能には、カルーセルそのものを左右にスワイプして動かすほか、独立した他のwidgetにCarouselController
オブジェクトを操作させて動かすものがあります
pub.devに記載のexampleでは以下のように使用されています
final CarouselController _controller = CarouselController();
carouselController: _controller,
操作widget
...Iterable<int>.generate(imgList.length).map(
(int pageIndex) => Flexible(
child: ElevatedButton(
onPressed: () => _controller.animateToPage(pageIndex),
child: Text("$pageIndex"),
),
),
),
このままで操作は可能なのですが、選択されている(アクティブな)操作widgetを明示したい場合があります
その際に以下のように、外部のstate
などで選択されているインデックスを保存しそれが選択されたインデックスと同じか判定することで実装するとします
int selectedIndex = 0;
void onPressed(int pageIndex) {
setState(() {
selectedIndex = pageIndex;
});
this._controller.animateToPage(pageIndex);
}
//~~~略~~~
...Iterable<int>.generate(widget.images!.length).map(
(int pageIndex) => Flexible(
child: TextButton(
onPressed: () {
onPressed(pageIndex);
},
child: Container(
decoration: pageIndex == selectedIndex
? BoxDecoration(border: Border.all(width: 2))
: null,
child: Image.network(
widget.images![pageIndex],
),
),
),
),
),
//~~~
この実装で、controllerを用いてカルーセルを動かした際に選択されたwidgetがアクティブであると表示できました
しかしこのままではcontrollerではなくカルーセル部分をスワイプしてカルーセルを切り替えるとselectedIndex
が切り替わらずアクティブの表示ができない問題が発生します
そこで、カルーセルからの操作を検知してアクティブなインデックスを切り替えるように追記します
CarouselOptions
のonPageChanged
を記述することでこのような変更を受け取って実行される関数を指定できます
CarouselSlider(
options: CarouselOptions(
onPageChanged: (index, reason) => {
onPressed(index)
},
このように書くとカルーセルが動いた際に動いた直後のインデックスを引数として更新する関数を実行します
そのため、実際にはこのコードだと
「コントローラーから移動先を指定する」
↓
「指定通りにカルーセルが動き出す」
↓
「カルーセルが動き出したためonPageChanged
が発火」
↓
「ここでインデックスに渡されるのは動き出した先のカルーセルなので指定に関係なくすぐ横のカルーセルにpageindex
が更新される」
といった流れで操作すると強制的に次のページになってしまう減少が発生します
これを避けるため、カルーセルが更新されたときの条件として、コントローラー経由の際には自動で更新を行わないよう追記します
onPageChanged
の第二引数でreason
というものがあり、具体的には
enum CarouselPageChangedReason { timed, manual, controller }
として3種類の値が定義されるCarouselPageChangedReason
オブジェクトを受け取れます
これを用いて次のように記述すると以上の問題が解決されます
onPageChanged: (index, reason) => {
if (reason == CarouselPageChangedReason.timed ||
reason == CarouselPageChangedReason.manual)
{onPressed(index)}
},