美文网首页
关于NestedScrollView + SliverAppBa

关于NestedScrollView + SliverAppBa

作者: 91阿生 | 来源:发表于2021-02-20 14:45 被阅读0次

    效果图:


    Sliver.gif
    class SliverPage extends StatefulWidget {
      static const routeName = '/sliver';
    
      @override
      _SliverPageState createState() => _SliverPageState();
    }
    
    class _SliverPageState extends State<SliverPage> with TickerProviderStateMixin {
      TabController _controller;
      ScrollController _scrollController; // 用于监听滚动offsetY
    
      final List<String> _tabTitles = ["关注", "预测"];
      final double _expandedHeight = 195; // SliverAppBar属性中可扩展的高度
      bool _isScrollTop = false; // 用于页面滚动是否到指定位置,显示导航栏中组件的作用
    
    
      @override
      void initState() {
        super.initState();
    
        _controller = TabController(length: _tabTitles.length, vsync: this);
        _scrollController = ScrollController();
    
        double dividingValue = _expandedHeight - 50;
        // 滚动监听
        _scrollController.addListener(() {
          if ((_scrollController.offset >= dividingValue) && _isScrollTop == false) {
            setState(() {
              _isScrollTop = true;
            });
          } else if ((_scrollController.offset < dividingValue) && _isScrollTop == true) {
            setState(() {
              _isScrollTop = false;
            });
          }
        });
      }
    
    // 销毁
    @override
    void dispose() {
      _controller.dispose();
      _scrollController.dispose();
      super.dispose();
    }
    
    @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: NestedScrollView(
            controller: _scrollController, // 监听滚动
            headerSliverBuilder: (context, innerBoxIsScrolled) {
              return [ // 只能存放Sliver类型的Widget;类似Container可用SliverToBoxAdapter()
                // 背景、顶部右侧按钮、头像+文本、水平可滚动列表
                SliverHeadView(expandedHeight: _expandedHeight, isScrollTop: _isScrollTop),
    
                // 吸顶效果的TabBar
                buildTabBar()
              ];
            }, 
            body: TabBarView( // 设置对应的页面
              controller: _controller,
              children: [
                FoucsPage(),
                PredictPage()
              ]
            )
          )
        );
      }
    
    

    组件 SliverHeadView()

    class SliverHeadView extends StatelessWidget {
      final bool isScrollTop;
      final double expandedHeight;
    
      SliverHeadView({
        @required this.expandedHeight,
        this.isScrollTop = false,
      });
    
    
      final List<String> _optionTitles = ["史蒂芬·库里", "迈克尔·乔丹", "科比·布莱恩特", "凯文·杜兰特", "德里克·罗斯"];
      final List<String> _optionImgs = ["Curry", "Jordan", "KB", "KD", "Rose"];
    
      @override
      Widget build(BuildContext context) {
        return SliverAppBar(
          elevation: 0.0,
          pinned: true,
          expandedHeight: expandedHeight,
          backgroundColor: Color(0xFFF5ca2b),
          automaticallyImplyLeading: false, // 去除默认系统的返回健
          leading: buildLeading(),
          actions: buildActions(),
          flexibleSpace: FlexibleSpaceBar(
            background: Container(
              color: Colors.white,
              child: Stack(
                children: [
                  // 背景图片
                  buildBg(),
                  // 顶部右侧按钮
                  buildRightButton(),
                  // 头像 + 文本
                  buildImageText(),
                  // 水平可滚动列表
                  buildOptions()
                ],
              ),
            ),
          )
        );
      }
    
      // 导航栏左侧缩放动画头像
      Widget buildLeading() {
        return ScaleAnimated(
          display: isScrollTop,
          child: CircleImage(
            image: AssetImage("assets/images/others_mi.jpg"),
            imageWH: 40,
            borderColor: Colors.white,
            borderWidth: 2,
          )
        );
      }
    
      // 导航栏右侧缩放按钮
      List<Widget> buildActions() {
        return [
          ScaleAnimated(
            display: isScrollTop,
            child: Padding(
              padding: const EdgeInsets.only(right: 15),
              child: Container(
                padding: EdgeInsets.fromLTRB(18, 10, 15, 10),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(18)
                ),
                child: Row(
                  children: [
                    Text("NBA资料 ", style: TextStyle(
                      color: Colors.black,
                      fontSize: 13
                    )),
                    Icon(Icons.arrow_forward_ios, color: Colors.black, size: 13,)
                  ],
                ),
              ),
            )
          )
        ];
      }
    
      // 背景图片
      Widget buildBg() {
        return Positioned(
          top: 0,
          left: 0,
          right: 0,
          child: Image.asset("assets/images/others_bg.png", fit: BoxFit.cover)
        );
      }
    
      // 顶部右侧按钮
      Widget buildRightButton() {
        return Positioned(
          right: 15,
          top: 35,
          child: FlatButton(
            color: Colors.white,
            highlightColor: Colors.transparent,
            splashColor: Colors.transparent,
            child: Row(
              children: [
                Text("NBA资料 ", style: TextStyle(
                  color: Colors.black,
                  fontSize: 13
                )),
                Icon(Icons.arrow_forward_ios, color: Colors.black, size: 13,)
              ],
            ),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(18.0),
            ),
            onPressed: () {
              print("顶部资料");
            },
          )
        );
      }
    
      // 头像+文本
      Widget buildImageText() {
        return Positioned(
          left: 20,
          top: 90,
          child: Container(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                CircleImage(
                  image: AssetImage("assets/images/others_mi.jpg"),
                  imageWH: 60,
                  borderColor: Colors.white,
                  borderWidth: 2,
                ),
                SizedBox(width: 15,),
                Text("NBA小子", style: TextStyle(
                  fontSize: 18,
                  color: Colors.black,
                  fontWeight: FontWeight.bold
                ))
              ],
            ),
          ) 
        );
      }
    
      // 水平可滚动列表
      Widget buildOptions() {
        return Positioned(
          left: 20,
          right: 20,
          top: 170,
          child: Container(
            height: 65,
            padding: EdgeInsets.only(top: 5, bottom: 5),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8)),
              boxShadow: [
                BoxShadow(color: Colors.white, offset: Offset(-1, -2), blurRadius: 10, spreadRadius: -5),
                BoxShadow(color: Colors.white, offset: Offset(1, -2), blurRadius: 10, spreadRadius: -5)
              ]
            ),
            child: ListView(
              shrinkWrap: true,
              scrollDirection: Axis.horizontal,
              children: [0, 1, 2, 3, 4].map((index) {
                return Container(
                  padding: EdgeInsets.symmetric(horizontal: 8),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: [
                      Image.asset("assets/images/${_optionImgs[index]}.png", fit: BoxFit.cover, width: 35, height: 35,),
                      Text("${_optionTitles[index]}", style: TextStyle(
                        fontSize: 13,
                        color: Colors.black
                      ))
                    ],
                  ),
                );
              }).toList(),
            ),
          )
        );
      }
    }
    

    关于缩放动画组件

    class ScaleAnimated extends StatelessWidget {
      final Widget child;
      final bool display;
    
      ScaleAnimated({
        @required this.child,
        this.display = false
      });
    
      @override
      Widget build(BuildContext context) {
        return AnimatedSwitcher(
          duration: Duration(milliseconds: 300),
          transitionBuilder: (child, animation) {
            return ScaleTransition(
              scale: animation,
              child: child,
            );
          },
          child: display ? child : SizedBox.shrink(),
        );
      }
    }
    

    关于吸顶效果组件 利用SliverPersistentHeader

    // 吸顶效果的TabBar
      Widget buildTabBar() {
        return SliverPersistentHeader( // SliverPersistentHeader:吸顶效果
          pinned: true,
          delegate: CeilingHeaderDelegate(
            child: TabBar(
              controller: _controller,
              labelColor: Color(0xFFF5ca2b),
              labelStyle: TextStyle(
                fontSize: 17,
                fontWeight: FontWeight.bold
              ),
              unselectedLabelColor: Color(0xff999999),
              indicatorSize: TabBarIndicatorSize.label,
              indicatorColor: Color(0xFFF5ca2b),
              indicatorWeight: 3,
              tabs: _tabTitles.map((value) => Tab(text: value)).toList()
            )
          ), // 不能直接实现,代理为抽象类;所以继承此代理
        );
      }
    }
    
    
    // 吸顶效果
    class CeilingHeaderDelegate extends SliverPersistentHeaderDelegate {
      final TabBar child;
    
      CeilingHeaderDelegate({
        @required this.child
      });
      
      @override
      Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
          return Container(
            color: Colors.white,
            child: child,
          );
        }
      
        @override
        double get maxExtent => this.child.preferredSize.height;
      
        @override
        double get minExtent => this.child.preferredSize.height;
      
        @override
        bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
        return true;
      }
    }
    

    相关文章

      网友评论

          本文标题:关于NestedScrollView + SliverAppBa

          本文链接:https://www.haomeiwen.com/subject/ekvtfltx.html