0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter】縮小するヘッダ + タブ + ListView の実装方法

Posted at

タイトルがわかりにくいですが、下のgifのようなUIをFlutterでどのように実現するかという話です。

result.gif

実装方法

全体の構造

NestedScrollViewを使って以下のようなViewの構造にします。

  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: DefaultTabController(
          length: 2,
          child: NestedScrollView(
            headerSliverBuilder: (context, hoge) {
              return [
                SliverPersistentHeader(
                    pinned: true,
                    // floating: true,
                    delegate: MySliverPersistentHeaderDelegate())
              ];
            },
            body: TabBarView(
              children: [
                ListView.builder(
                  key: PageStorageKey(0), // keep scroll position
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text("page one:$index"),
                    );
                  },
                  itemCount: 50,
                ),
                ListView.builder(
                  key: PageStorageKey(1),
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text("page two:$index"),
                    );
                  },
                  itemCount: 80,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

SliverAppBarが便利そうだったのですが、bottomのタブだけ残すという動作ができなかったのでヘッダは SliverPersistentHeader を使って、タブだけ残るようにしています。

MySliverPersistentHeaderDelegate

MySliverPersistentHeaderDelegateは以下のような実装にしています。
スクロールによって緑の部分の高さを減らしていって、最終的にタブの部分だけ残るようにしています。
サンプルなので常にリビルドしています。

class MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  @override
  double get maxExtent => 200;

  @override
  double get minExtent => 48; // from tab height

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    print("so:$shrinkOffset, oc:$overlapsContent");
    return Container(
      color: Colors.pinkAccent,
      height: max(maxExtent - shrinkOffset, minExtent),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
            color: Colors.green,
            height: max(maxExtent - shrinkOffset - minExtent, 0),
          ),
          TabBar(tabs: [
            Tab(
              text: "page1",
            ),
            Tab(
              text: "page2",
            ),
          ]),
        ],
      ),
    );
  }

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

まとめ

この手のUIをiOS/Androidで両方実現しようとすると、どちらにしても結構コスト掛かりそうですがFlutterでそれなりに簡単に両OSに対応できるのは素晴らしいですね。

サンプルプロジェクトはこちら

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?