关于NestedScrollView + SliverAppBa

2021-02-20  本文已影响0人  91阿生

效果图:


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;
  }
}
上一篇下一篇

猜你喜欢

热点阅读