Flutter滑动吸顶tab实现

NestedScrollView实现
import 'package:flutter/material.dart';
import 'package:meowfront/constant/constant.dart';
import 'package:meowfront/pages/usertabs/LikePage.dart';
import 'package:meowfront/pages/usertabs/PostPage.dart';

class UserPage extends StatefulWidget {
  
  _UserPageState createState() => _UserPageState();
}

class _UserPageState extends State<UserPage>
    with SingleTickerProviderStateMixin {
  ScrollController scrollController = ScrollController();

  bool isShowBlackTitle = false;

  TabController? _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, initialIndex: 0, length: 3);
  }

  
  void dispose() {
    super.dispose();
    _tabController?.dispose();
  }

  //判断滚动改变透明度
  void _onScroll(offset) {
    if (offset > 100) {
      setState(() {
        isShowBlackTitle = true;
      });
    } else {
      setState(() {
        isShowBlackTitle = false;
      });
    }
  }

  Column _timeSelection() {
    return Column(
      children: <Widget>[
        Stack(
          alignment: Alignment.topLeft,
          children: <Widget>[
            Container(
              height: 50,
              color: Colors.white,
            ),
            Container(
              alignment: Alignment.center,
              padding: EdgeInsets.symmetric(horizontal: 70),
              child: TabBar(
                tabs: [
                  Tab(text: '帖子'),
                  Tab(text: '点赞'),
                  Tab(text: '收藏'),
                ],
                controller: _tabController,
                isScrollable: true, //如果多个按钮可以滑动
                indicatorColor: Colors.blue, //指示器颜色
                labelColor: Colors.black, //指示器文字颜色
                unselectedLabelColor: Colors.black, //未选中的指示器文字颜色
                indicatorSize: TabBarIndicatorSize.label,
              ),
            )
          ],
        ),
        Container(
          height: 0.5,
          color: Colors.black12,
        ),
      ],
    );
  }

  
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
          backgroundColor: Colors.white,
          body: Stack(children: [
            NotificationListener(
                onNotification: (scrollNotification) {
                  if (scrollNotification is ScrollUpdateNotification &&
                      scrollNotification.depth == 0) {
                    //滚动并且是列表滚动的时候
                    _onScroll(scrollNotification.metrics.pixels);
                  }
                  return true;
                },
                child: Stack(
                  children: <Widget>[
                    NestedScrollView(
                      headerSliverBuilder:
                          (BuildContext context, bool innerBoxIsScrolled) {
                        return <Widget>[
                          SliverOverlapAbsorber(
                            handle:
                                NestedScrollView.sliverOverlapAbsorberHandleFor(
                                    context),
                            sliver: SliverAppBar(
                              leading: new Container(
                                margin: EdgeInsets.only(top: 20, bottom: 10),
                                child: isShowBlackTitle
                                    ? Image.asset(
                                        Constant.ASSETS_IMG +
                                            'userinfo_icon_back_black.png',
                                        fit: BoxFit.fitHeight,
                                      )
                                    : Image.asset(
                                        Constant.ASSETS_IMG +
                                            'userinfo_icon_back_white.png',
                                        fit: BoxFit.fitHeight,
                                      ),
                              ),
                              title: isShowBlackTitle ? Text('名字') : Text(''),
                              centerTitle: true,
                              pinned: true,
                              floating: false,
                              snap: false,
                              primary: true,
                              expandedHeight: 210.0,
                              backgroundColor: Color(0xffF8F8F8),
                              elevation: 0,
                              //是否显示阴影,直接取值innerBoxIsScrolled,展开不显示阴影,合并后会显示
                              forceElevated: innerBoxIsScrolled,
                              actions: <Widget>[
                                new Container(
                                  margin: EdgeInsets.only(
                                      right: 10, top: 20, bottom: 10),
                                  child: isShowBlackTitle
                                      ? Image.asset(
                                          Constant.ASSETS_IMG +
                                              'userinfo_search_black.png',
                                          width: 30,
                                          height: 30,
                                          fit: BoxFit.cover,
                                        )
                                      : Image.asset(
                                          Constant.ASSETS_IMG +
                                              'userinfo_search_white.png',
                                          width: 30,
                                          height: 30,
                                          fit: BoxFit.cover,
                                        ),
                                ),
                                new Container(
                                  margin: EdgeInsets.only(
                                      right: 10, top: 20, bottom: 10),
                                  child: isShowBlackTitle
                                      ? Image.asset(
                                          Constant.ASSETS_IMG +
                                              'userinfo_more_black.png',
                                          width: 30,
                                          height: 30,
                                          fit: BoxFit.cover,
                                        )
                                      : Image.asset(
                                          Constant.ASSETS_IMG +
                                              'userinfo_more_white.png',
                                          width: 30,
                                          height: 30,
                                          fit: BoxFit.cover,
                                        ),
                                ),
                              ],

                              flexibleSpace: FlexibleSpaceBar(
                                collapseMode: CollapseMode.pin,
                                background: new Column(
                                  children: <Widget>[
                                    new Container(
                                      height: 210,
                                      decoration: BoxDecoration(
                                        image: DecorationImage(
                                          image: AssetImage(
                                            Constant.ASSETS_IMG +
                                                'ic_personinfo_bg4.png',
                                          ),
                                          fit: BoxFit.cover,
                                        ),
                                      ),
                                      child: new Column(
                                        children: <Widget>[
                                          Container(
                                            margin: EdgeInsets.only(top: 25),
                                            child: new Row(
                                              mainAxisAlignment:
                                                  MainAxisAlignment.start,
                                              children: <Widget>[
                                                Expanded(
                                                  child: Container(
                                                    alignment: Alignment.center,
                                                    child: new CircleAvatar(
                                                      backgroundImage:
                                                          new NetworkImage(
                                                              "https://cdn.stocksnap.io/img-thumbs/960w/flower-bouquet_X6UBKGVU0Z.jpg"),
                                                      radius: 33.0,
                                                    ),
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                          Row(
                                            mainAxisAlignment:
                                                MainAxisAlignment.center,
                                            children: <Widget>[
                                              Container(
                                                margin:
                                                    EdgeInsets.only(top: 15),
                                                child: Text(
                                                  "名字",
                                                  style: TextStyle(
                                                      color: Colors.white,
                                                      fontSize: 17),
                                                ),
                                              ),
                                            ],
                                          )
                                        ],
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                          SliverPersistentHeader(
                            delegate: _SliverAppBarDelegate2(_timeSelection()),
                            pinned: true,
                          ),
                        ];
                      },
                      body: Container(
                        margin: EdgeInsets.all(0),
                        child: TabBarView(
                          controller: _tabController,
                          children: [
                            PostPage(),
                            LikePage(),
                            PostPage(),
                          ],
                        ),
                      ),
                    ),
                  ],
                )),
          ]),
        ));
  }
}

class _SliverAppBarDelegate2 extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate2(this._tabBar);

  final Column _tabBar;

  
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return _tabBar;
  }

  
  double get maxExtent => 52;

  
  double get minExtent => 52;

  
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

存在的问题:body部分顶部会被header遮挡

在这里插入图片描述
在这里插入图片描述

ExtendedNestedScrollView实现
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart';
import 'package:meowfront/constant/constant.dart';
import 'package:meowfront/pages/usertabs/CollectPage.dart';
import 'package:meowfront/pages/usertabs/LikePage.dart';
import 'package:meowfront/pages/usertabs/PostPage.dart';
import 'package:meowfront/util/ScreenAdapter.dart';

class UserPage extends StatefulWidget {
  
  State<UserPage> createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> with TickerProviderStateMixin {
  late TabController _tabC;
  bool isShowBlackTitle = false;
  List<String> pageTitles = ['帖子', '点赞', '收藏'];
  List<Widget> pageList = [PostPage(), LikePage(), CollectPage()];
  
  void initState() {
    super.initState();
    _tabC = TabController(
      length: 3,
      initialIndex: 0,
      vsync: this,
    );
  }

//判断滚动改变透明度
  void _onScroll(offset) {
    if (offset > ScreenAdapter.heigth(100)) {
      setState(() {
        isShowBlackTitle = true;
      });
    } else {
      setState(() {
        isShowBlackTitle = false;
      });
    }
  }

  
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            body: Stack(
      children: [
        NotificationListener(
            onNotification: (scrollNotification) {
              if (scrollNotification is ScrollUpdateNotification &&
                  scrollNotification.depth == 0) {
                //滚动并且是列表滚动的时候
                _onScroll(scrollNotification.metrics.pixels);
              }
              return true;
            },
            child: _contentView(context))
      ],
    )));
  }

  _contentView(context) {
    double statusBarHeight = MediaQuery.of(context).padding.top;
    double pinnedHeaderHeight = kToolbarHeight;
    return ExtendedNestedScrollView(
      headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[
          SliverAppBar(
            automaticallyImplyLeading: false,
            // leading: new Container(
            //     margin: EdgeInsets.only(top: 20, bottom: 10),
            //     child: InkWell(
            //       onTap: () {

            //       },
            //       child: isShowBlackTitle
            //           ? Image.asset(
            //               Constant.ASSETS_IMG + 'userinfo_icon_back_black.png',
            //               fit: BoxFit.fitHeight,
            //             )
            //           : Image.asset(
            //               Constant.ASSETS_IMG + 'userinfo_icon_back_white.png',
            //               fit: BoxFit.fitHeight,
            //             ),
            //     )),
            title: isShowBlackTitle ? Text('名字') : Text(''),
            centerTitle: true,
            pinned: true,
            floating: false,
            snap: false,
            primary: true,
            expandedHeight: ScreenAdapter.heigth(300),
            backgroundColor: Color(0xffF8F8F8),
            elevation: 0,
            //是否显示阴影,直接取值innerBoxIsScrolled,展开不显示阴影,合并后会显示
            forceElevated: innerBoxIsScrolled,
            actions: <Widget>[
              Container(
                margin: EdgeInsets.only(right: 10, top: 20, bottom: 10),
                child: isShowBlackTitle
                    ? Image.asset(
                        Constant.ASSETS_IMG + 'userinfo_search_black.png',
                        width: 30,
                        height: 30,
                        fit: BoxFit.cover,
                      )
                    : Image.asset(
                        Constant.ASSETS_IMG + 'userinfo_search_white.png',
                        width: 30,
                        height: 30,
                        fit: BoxFit.cover,
                      ),
              ),
              Container(
                margin: EdgeInsets.only(right: 10, top: 20, bottom: 10),
                child: isShowBlackTitle
                    ? Image.asset(
                        Constant.ASSETS_IMG + 'userinfo_more_black.png',
                        width: 30,
                        height: 30,
                        fit: BoxFit.cover,
                      )
                    : Image.asset(
                        Constant.ASSETS_IMG + 'userinfo_more_white.png',
                        width: 30,
                        height: 30,
                        fit: BoxFit.cover,
                      ),
              ),
            ],

            flexibleSpace: FlexibleSpaceBar(
              collapseMode: CollapseMode.pin,
              background: Column(
                children: <Widget>[
                  Container(
                    height: ScreenAdapter.heigth(300),
                    decoration: BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage(
                          Constant.ASSETS_IMG + 'user_bg.png',
                        ),
                        fit: BoxFit.cover,
                      ),
                    ),
                    child: Column(
                      children: <Widget>[
                        Container(
                          padding:
                              EdgeInsets.only(top: ScreenAdapter.heigth(25)),
                          child: new Row(
                            mainAxisAlignment: MainAxisAlignment.start,
                            children: <Widget>[
                              Expanded(
                                child: Container(
                                  alignment: Alignment.center,
                                  child: new CircleAvatar(
                                    backgroundImage: new NetworkImage(
                                        "https://cdn.stocksnap.io/img-thumbs/960w/flower-bouquet_X6UBKGVU0Z.jpg"),
                                    radius: 33.0,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: <Widget>[
                            Container(
                              margin: EdgeInsets.only(top: 15),
                              child: Text(
                                "名字",
                                style: TextStyle(
                                    color: Colors.white, fontSize: 17),
                              ),
                            ),
                          ],
                        )
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              height: 0,
              child: Row(
                children: [Text('')],
              ),
            ),
          )
        ];
      },
      pinnedHeaderSliverHeightBuilder: () {
        return pinnedHeaderHeight;
      },
      onlyOneScrollInBody: true,
      body: Container(
        width: double.infinity,
        height: double.infinity,
        color: Colors.white,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TabBar(
              isScrollable: true, //如果多个按钮可以滑动
              indicatorColor: Colors.blue, //指示器颜色
              labelColor: Colors.black, //指示器文字颜色
              unselectedLabelColor: Colors.black, //未选中的指示器文字颜色
              indicatorSize: TabBarIndicatorSize.label,

              controller: _tabC,
              tabs: pageTitles.map((e) => Tab(text: '$e')).toList(),
            ),
            Container(
              height: 0.5,
              color: Colors.black12,
            ),
            Expanded(
              child: TabBarView(
                  controller: _tabC,
                  physics: NeverScrollableScrollPhysics(),
                  children: pageList),
            ),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter实现底部Tab切换可以使用Scaffold的bottomNavigationBar属性,该属性接受一个BottomNavigationBar类型的对象。BottomNavigationBar是底部导航条,可以帮助我们实现底部Tab切换。\[1\] 下面是一个示例代码,展示了如何定义底部导航条的按钮集合和当前展示的tab页面索引: ```dart List<BottomNavigationBarItem> _barItem = \[ BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首页')), BottomNavigationBarItem(icon: Icon(Icons.list), title: Text('任务')), BottomNavigationBarItem(icon: Icon(Icons.access_time), title: Text('计划')), \]; int _currentIndex = 0; // 当前展示的tab页面索引 ``` 在Scaffold的bottomNavigationBar属性中,我们可以设置items为_barItem,currentIndex为_currentIndex,以及onTap回调函数来处理点击导航栏子项的事件。例如: ```dart Scaffold( // ... bottomNavigationBar: BottomNavigationBar( items: _barItem, currentIndex: _currentIndex, onTap: (index) { setState(() { _currentIndex = index; }); }, ), // ... ) ``` 这样就可以实现底部Tab切换的功能了。 #### 引用[.reference_title] - *1* *2* *3* [Flutter 底部导航栏(Tab 页)的快速实现](https://blog.csdn.net/u011578734/article/details/110523298)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值