まえがき
2020年入ってからFlutterの勉強を初めてきました。
今後いろんなパッケージをもっと勉強していきたいと思ってるので、人気なものをアウトプットとして記事にしていきたいと思います。
今回はcarousel_sliderと言うパッケージを紹介します。
これまで載せた記事
- shared_preferencesの紹介
- url_launcherの紹介
- flutter_slidable
- carousel_slider←いまこれ
carousel_slider
carousel_sliderはいくつかのWidgetをスライドさせて表示できるようにするWidgetです。
画像などをスライドさせてみることができます。
手動でスライドするのと、自動でスライドするのを設定できたりもします。
サンプル
GridView
で並べられた画像をタップすると、その画像が拡大されて横にスライドさせると他の画像が表示できるようにしました。
まずは何でもいいので画像を数枚用意してください。
imagesディレクトリを作り、
sample1.png
sample2.png
sample3.png
sample4.png
sample5.png
と名前の画像ファイルを入れました。
画像の表示の仕方は今回は説明してないので、わからない人は検索してみてください。
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
class ImageList extends StatefulWidget {
@override
State<StatefulWidget> createState() => _ImageList();
}
class _ImageList extends State<ImageList> {
final List images = [
"images/sample1.png",
"images/sample2.png",
"images/sample3.png",
"images/sample4.png",
"images/sample5.png",
"images/sample6.png"
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('画像を表示'),
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: images.length,
itemBuilder: (BuildContext context, int index) {
return _photoItem(images[index], index);
},
),
);
}
Widget _photoItem(String image, int index) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(border: Border.all(color: Colors.grey)),
child: Image.asset(
image,
fit: BoxFit.cover,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SampleCarouselSlider(images: images, index: index),
fullscreenDialog: true,
),
);
},
);
}
}
class SampleCarouselSlider extends StatefulWidget {
final List images;
final int index;
SampleCarouselSlider({this.images, this.index});
@override
State<StatefulWidget> createState() => _SampleCarouselSlider(images, index);
}
class _SampleCarouselSlider extends State<SampleCarouselSlider> {
List images;
int index;
_SampleCarouselSlider(this.images, this.index);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('carousel_slider'),
),
body: CarouselSlider.builder(
options: CarouselOptions(
height: 600.0,
initialPage: index,
viewportFraction: 1,
enableInfiniteScroll: false,
),
itemCount: images.length,
itemBuilder: (BuildContext context, int index) {
return Image.asset(
images[index],
);
},
),
);
}
}
説明
画像をタップすると、画像のパスが入ったリストとその画像の順番を引数で渡しSampleCarouselSlider
と言うStatefulWidget
が立ちます。
今回はFirestoreやFirebase Storageから画像を取得した時にも応用できるようにCarouselSlider.builder()
で作りました。
itemCount
やitemBuilder
はListView.builder()
と同じですね。
CarouselOptions()
が重要で様々なプロパティがあります。
まずは今回のサンプルで使っているものです
-
height
:高さ -
initialPage
:表示される最初のページ。本サンプルではindexでリストの番号を渡しているためタップした画像から始まっている -
viewportFraction
:各ページが占めるビューポート(表示領域)の割合。小さくすると隣のページが見えるように画像が小さくなり、大きくすればするほど隣とのページが離れる。1~0.8がおすすめ -
enableInfiniteScroll
:最後のページ⇄最初のページの遷移が可能かどうか。デフォルトはtrue
このようになっています。高さやビューポートの割合は、言葉の説明より、数字をいじってみることで理解した方が早いと思います。
ここからは他にもCarouselOptions()
はこんなことができるよと言うのを紹介します。
-
aspectRatio
画面のアスペクト比。高さが指定されてない時に用いられる。デフォルトでは16/9 -
scrollDirection
:スクロールの向きAxis.horizontal
で横、Axis.vertical
で縦。デフォルトでは横向き -
reverse
:ページの順番を逆にする。デフォルトはfalse
-
autoPlay
:自動でスライドしてくれるかどうか。デフォルトはfalse
-
autoPlayInterval
:自動スライドのインターバル時間、Duration()
で指定。デフォルトは4秒。 -
autoPlayAnimationDuration
:自動スライドがが始まって終わるまでの時間。(スライドの速さ)Duration
で指定。デフォルトで0.8秒(milliseconds: 100)と表記 -
autoPlayCurve
:自動スライドのアニメーションの違い。Curves
で指定。たくさんあるのでCurvesの公式ドキュメントでアニメーションの詳細を確認してください。デフォルトではCurves.fastOutSlowIn
このような感じにいろいろ設定できます。
またCarouselSlider.builder
ではなく実装するときはこんな感じにできます。
これをbodyの中に置き換えても問題ありません。
CarouselSlider(
options: CarouselOptions(
height: 600.0,
initialPage: index,
viewportFraction: 1,
enableInfiniteScroll: false,
reverse: false,
),
items: images.map((i) {
return Builder(
builder: (BuildContext context) {
return Image.asset(i);
},
);
}).toList(),
)
パッケージソースコードを読んだ感想
なんかすげい難しいことをしてるって感じではないのかな?って思った
アニメーション作るのは難しいかもしれないけど、
CarouselOptions
クラスは変数定義して初期値入れてるだけ
CarouselSlider
クラスは条件分岐がたくさんあって、訳わかんなくなるけどなんとなくわかる
自動スライドとか作ってるから複雑な感じになってるけど、ページを複数用意してスクロールさせるのはそんな難しくないのかもしれないと思いました。
あと一つ勉強になったのは
Widget wrapper;
if (widget.options.height != null) {
wrapper = Container(height: widget.options.height, child: child);
} else {
wrapper =
AspectRatio(aspectRatio: widget.options.aspectRatio, child: child);
}
こんな感じにWidgetを変数で定義して条件分岐で中身変えてすぐしたのchild
でwrapper
使ってたこと。すっきり見える
ちなみにこれはCarouselOptions
でheight
が指定されてるかどうかの条件分岐ですね。