Flutter 滑動列表隱藏頭部 ListView+TabBar懸浮的實現

jlanglang發表於2019-05-16

先來張效果圖

在這裡插入圖片描述

我的需求是,列表滾動到頂部,Tabbar停留,置頂.

實際寫起來,繞了不少彎路.


最開始我使用的:

CustomScrollView

程式碼如下:

import 'package:flutter/material.dart';


const url =
    'http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg';

class TestPage2 extends StatefulWidget {
  @override
  _TestPage2State createState() => _TestPage2State();
}

class _TestPage2State extends State<TestPage2> {
  var tabTitle = [
    '頁面1',
    '頁面2',
    '頁面3',
  ];

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
        length: tabTitle.length,
        child: Scaffold(
          body: new CustomScrollView(
            slivers: <Widget>[
              new SliverAppBar(
                expandedHeight: 200.0,
                floating: true,
                pinned: true,
                flexibleSpace: FlexibleSpaceBar(
                    centerTitle: true,
                    title: Text(
                      "我是可以跟著滑動的title",
                    ),
                    background: Image.network(
                      url,
                      fit: BoxFit.cover,
                    )),
              ),
              new SliverToBoxAdapter(
                child: new TabBar(
                  tabs: tabTitle.map((f) => Tab(text: f)).toList(),
                  indicatorColor: Colors.red,
                  unselectedLabelColor: Colors.black,
                  labelColor: Colors.red,
                ),
              ),
              new SliverFillRemaining(
                child: TabBarView(
                  children: tabTitle
                      .map((s) => ListView.builder(
                            itemBuilder: (context, int) => Text("123"),
                            itemCount: 50,
                          ))
                      .toList(),
                ),
              )
            ],
          ),
        ));
  }
}


複製程式碼

在這裡插入圖片描述
雖然列表效果出來了,但不是我想要的,

列表滑動,不會聯動

使用SliverToBoxAdapter 並不會有聯動效果.因為它是固定的.

想要聯動,只有像NestedScrollView原始碼裡的

在這裡插入圖片描述
使用自定義的PrimaryScrollController才行. 不過既然NestedScrollView已經做,不是特別的需求,就用NestedScrollView就好了


正確的姿勢 NestedScrollView:

SliverAppBar

相當於Appbar.不過是Sliver滾動家族裡的

  new SliverAppBar(
                  expandedHeight: 200.0,
                  floating: true,
                  pinned: true,
                  flexibleSpace: FlexibleSpaceBar(
                      centerTitle: true,
                      title: Text(
                        "我是可以跟著滑動的title",
                      ),
                      background: Image.network(
                        url,
                        fit: BoxFit.cover,
                      )),
                ),
複製程式碼

懸浮最關鍵的:SliverPersistentHeader

這個是可以聯動並且可以停留在頂部的 設定懸浮停留的屬性pinned. 這裡需要自己實現一個Delegate.因為需要停留weight的高度.

 new SliverPersistentHeader(
                  delegate: new SliverTabBarDelegate(
                    new TabBar(
                      tabs: tabTitle.map((f) => Tab(text: f)).toList(),
                      indicatorColor: Colors.red,
                      unselectedLabelColor: Colors.black,
                      labelColor: Colors.red,
                    ),
                    color: Colors.white,
                  ),
                  pinned: true,
                ),
複製程式碼

最終程式碼:

import 'package:flutter/material.dart';

const url =
    'http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg';

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  var tabTitle = [
    '頁面1',
    '頁面2',
    '頁面3',
  ];

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
        length: tabTitle.length,
        child: Scaffold(
          body: new NestedScrollView(
            headerSliverBuilder: (context, bool) {
              return [
                SliverAppBar(
                  expandedHeight: 200.0,
                  floating: true,
                  pinned: true,
                  flexibleSpace: FlexibleSpaceBar(
                      centerTitle: true,
                      title: Text(
                        "我是可以跟著滑動的title",
                      ),
                      background: Image.network(
                        url,
                        fit: BoxFit.cover,
                      )),
                ),
                new SliverPersistentHeader(
                  delegate: new SliverTabBarDelegate(
                    new TabBar(
                      tabs: tabTitle.map((f) => Tab(text: f)).toList(),
                      indicatorColor: Colors.red,
                      unselectedLabelColor: Colors.black,
                      labelColor: Colors.red,
                    ),
                    color: Colors.white,
                  ),
                  pinned: true,
                ),
              ];
            },
            body: TabBarView(
              children: tabTitle
                  .map((s) => ListView.builder(
                        itemBuilder: (context, int) => Text("123"),
                        itemCount: 50,
                      ))
                  .toList(),
            ),
          ),
        ));
  }
}

class SliverTabBarDelegate extends SliverPersistentHeaderDelegate {
  final TabBar widget;
  final Color color;

  const SliverTabBarDelegate(this.widget, {this.color})
      : assert(widget != null);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(
      child: widget,
      color: color,
    );
  }

  @override
  bool shouldRebuild(SliverTabBarDelegate oldDelegate) {
    return false;
  }

  @override
  double get maxExtent => widget.preferredSize.height;

  @override
  double get minExtent => widget.preferredSize.height;
}

複製程式碼

最終效果

在這裡插入圖片描述

題外話:

如果不使用TabBar+TabBarView 那麼直接使用CustomScrollView就可以了. 使用SliverList代替ListView就可以進行聯動.


期待你的留言交流.

交流群: 782978118(flutter群) 493180098 (android群)

flutter開源專案地址:github.com/Jlanglang/y…

相關文章