Flutter の carousel_slider を使ってみる
はじめに
このスクラップでは Flutter の carousel_slider を使ってカルーセルスライダーを実装してみる
前回
前回は Flutter の Cupertino Widgets を一通り使ってみた
準備コマンド
flutter create hello_carousel
cd hello_carousel
flutter pub add carousel_slider
Getting Started
コード
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: Center(
child: CarouselSlider(
options: CarouselOptions(
height: 400.0,
),
items: [1, 2, 3].map((i) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: Text('text $i', style: const TextStyle(fontSize: 16.0)),
);
}).toList(),
),
),
);
}
}
実行結果
サイズの調整
CarouselOptions
の viewportFraction
を設定することで変更できる
CarouselOptions(
height: 400.0,
viewportFraction: 0.9
)
CarouselOptions(
height: 400.0,
viewportFraction: 0.6
)
ところで MediaQuery とは何なのか?
わかりやすい Zenn 記事
わかりやすい動画
公式ドキュメント
推測するに下記のコードは画面のサイズを取得しているのだろう
MediaQuery.of(context).size.width
上記の代わりに double.infinity
でも良い気がするけどどうなんだろう
CarouselSlider(
options: CarouselOptions(
height: 400.0,
viewportFraction: 0.6
),
items: [1, 2, 3].map((i) {
return Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: Text('text $i', style: const TextStyle(fontSize: 16.0)),
);
}).toList(),
),
大丈夫みたいだけどなんとなく危ない感じがするのでやめておこう
次は無限スクロールを試してみる
まずは無限スクロールの認識が違っていた
carousel_slider では無限スクロールとは下図のようにループしないカルーセルのようだ
ループさせない場合は下記のように enableInfiniteScroll
を明示的に false
に設定する(デフォルトでは true
)
CarouselOptions(
enableInfiniteScroll: false,
)
でもほぼ無限にスクロールさせることはできる
CarouselSlider.builder
を使うとカルーセルページを動的に生成することもできる
CarouselSlider.builder(
options: CarouselOptions(
height: 400.0,
enableInfiniteScroll: false,
),
itemCount: 2,
itemBuilder: (context, index, realIndex) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: Text(
'index $index, realIndex $realIndex',
style: const TextStyle(fontSize: 16.0),
),
);
},
)
結果は下図の通り
itemCount
は必須の引数なので指定する必要があり、これでは無限にはできない感じがする
しかし itemBuilder
には realIndex
という引数があり、これを上手に使えばほぼ無限にスクロールさせることができそう
realIndex
は 10000 から始まって0が最小値であることを確認した
carousel_state.dart のソースコードの realPage
を変更すると楽に確認できる(1万回左にスクロールしても良いが...)
1万回も左にスクロールする人は稀だと思うので多くの場合はあまり気にしなくて良さそう
イベント
onPageChanged
オプションを使うことでページ変更時のイベントハンドラを設定できる
CarouselOptions(
height: 400.0,
onPageChanged: (index, reason) {
print('index $index, reason $reason');
},
),
flutter: index 0, reason CarouselPageChangedReason.manual
flutter: index 1, reason CarouselPageChangedReason.manual
残念ながらこちらは index
しか返してくれないので realIndex
は取得できない
showDialog と組み合わせる
Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: Container(),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return CarouselSlider.builder(
options: CarouselOptions(
height: 400.0,
onPageChanged: (index, reason) {
print('index $index, reason $reason');
},
),
itemCount: 2,
itemBuilder: (context, index, realIndex) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: Text(
'index $index, realIndex $realIndex',
style: const TextStyle(fontSize: 16.0),
),
);
},
);
},
);
},
label: const Text('Show carousel'),
),
)
表示自体はされたがテキストに赤い線が表示されていて何か足りない所がある様子だ
普通にコンテナをダイアログ表示させてもエラーは出る
showDialog(
context: context,
builder: (context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: const Text(
"I'm a container.",
style: TextStyle(fontSize: 16.0),
),
);
},
);
したがって CarouselSlider が悪い訳では無さそう
ダイアログを適切に表示させる方法について調べてみよう
参考になるリンク
Dialog
か SimpleDialog
を使えば良さそう
ダイアログとして表示できた
Dialog
クラスを使ったらモーダル風にカルーセルを表示できた
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.zero,
child: CarouselSlider.builder(
options: CarouselOptions(
height: 400.0,
),
itemCount: 10,
itemBuilder: (context, index, realIndex) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: Text(
'index $index, realIndex $realIndex',
style: const TextStyle(fontSize: 16.0),
),
);
},
),
);
},
);
backgroundColor
と insetPadding
を調整が必要
スライドとスライドの間を押してもモーダルが消えないのは悩ましいが仕方がない
最初のページを指定する
基本的なことですが initialPage
オプションを使用すると最初に表示されるページを指定できる
CarouselOptions(
height: 400.0,
initialPage: 5,
)
あまり関係ないですが
このスクラップを作成する前に table_calendar というのも触ってみたのだが、せっかくならスクラップとして残せばよかったな、失敗
おわりに
以上で一旦クローズ、次はフォームのバリデーションについて調べる