anz blog

スクロールしても画像が上部に固定して残るようにしたい

2019-06-23 #Flutter #Dart

ざっくりまとめ。

  • スクロールしても画像を画面上部にある程度大きさで残したい
  • SliverAppBar を使うとできた
    • pinned の設定をする
    • bottom の設定をする

です。

環境

  • Flutter v1.5.4-hotfix.2
  • Dart v2.3.0

やりたいこと

画像を表示してスクロールしても画面上部に固定されて残したい。
どんなもんかは見てもらったほうが早いので

こういうやつです。

実装

SliverAppBar というものを使います。
CustomScrollView で利用できる Widget のようなので、ListView を使っている場合は移行が必要。
とはいっても大体同じ構造で使えるので変えるのは簡単です。

まずは最小構成で

CustomScrollView(
    slivers: <Widget>[
        SliverAppBar(
            expandedHeight: 200,
            flexibleSpace: Image.asset('assets/cover.jpg', fit: BoxFit.cover),
        ),
        SliverList(
            delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
                return Container(padding: EdgeInsets.all(16.0), child: Text('Row_$index'));
            }),
        )
    ],
)

SliverAppBar をとりあえず使ってみようとした場合はこの様な感じでいけます。
これを実行すると...

これだけでも悪くはないですが、せっかく表示しているいい感じの画像がリストのスクロールとともに消えてしまうのが寂しいです(笑)

画像をのこす

SliverAppBar を上部に残し続けるのも簡単で pinned プロパティを設定してあげるとできます。

CustomScrollView(
    slivers: <Widget>[
        SliverAppBar(
            pinned: true,   // <- 追加
            expandedHeight: 200,
            flexibleSpace: Image.asset('assets/cover.jpg', fit: BoxFit.cover),
        ),
        SliverList(
            delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
                return Container(padding: EdgeInsets.all(16.0), child: Text('Row_$index'));
            }),
        )
    ],
)

pinned: true を追加しました。
これで実行すると....

スクロールしても SliverAppBar が消えることなく残り続けます。
だいぶ良くなってきましたが、これだと画像領域がちょっと小さすぎる。
画像なのだから NavigationBar の height よりかはも少しほしいところです。

minimum height の設定をする

とはいっても、minHeight みたいなプロパティはないのですこしひねった方法で実現します。
bottom というプロパティがあるので、こちらに最小の高さを維持できるように何もない Widget を設定しておくことで、
minHeight みたいな挙動をさせます。

CustomScrollView(
    slivers: <Widget>[
        SliverAppBar(
            pinned: true,
            expandedHeight: 200,
            flexibleSpace: Image.asset('assets/cover.jpg', fit: BoxFit.cover),
            bottom: PreferredSize(child: Text(''), preferredSize: Size.fromHeight(60),),    // <- 追加
        ),
        SliverList(
            delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
                return Container(padding: EdgeInsets.all(16.0), child: Text('Row_$index'));
            }),
        )
    ],
)

bottom: PreferredSize(child: Text(''), preferredSize: Size.fromHeight(60),) を追加しました。
この Size.fromHeight(60) の部分が最小の高さを維持する役割になります。
最終的にはこの値と NavigationBar のものを足した高さが残ることになります。

これで実行すると、最初に紹介したものになります👍

コード

一応サクッとコピペで試せるように全部のっけておきます。

参考