22、 Flutter Widgets 之 NestedScrollView 组件。

Flutter中常用的滑动布局 ScrollView 有 SingleChildScrollView、NestedScrollView、CustomScrollView。

SingleChildScrollView 用来处理简单可滑动的页面布局视图,如一般的数据详情页面,当内容足够多时,一屏显示不下时,就需要滑动处理。

NestedScrollView 滑动组件是用来处理复杂情况下的滑动应用场景,如向上滑动视图时,要折叠隐藏一部分内容,这时候就需要使用到 NestedScrollView 与 SliverAppBar 的结合使用。

CustomScrollView 用来处理更为复杂的布局结合 SliverAppBar,SliverList和SliverGrid SliverPadding SliverToBoxAdapter SliverPersistentHeader, SliverFillRemaining,SliverFillViewport等来使用。

如一个详情页面中 即需要 GridView 来实现二维宫格效果,也需要 ListView 列表效果,如下图所示的图片效果,当使用 CustomScrollView 结合 SliverList 和SliverGrid 就可轻松实现,当然结合一下 SliverAppBar 也能实现折叠效果的头部布局,所以说 CustomScrollView 很强大。

在实际应用开发中,如果只是一个简单的适配页面滑动,建议码农使用 SingleChildScrollView 就可以。
本章重点介绍NestedScrollView:

NestedScrollView


可以在其内部嵌套其他滚动视图的组件,其滚动位置是固有链接的。

在普通的ScrollView中, 如果有一个Sliver组件容纳了一个TabBarView,它沿相反的方向滚动(例如,允许用户在标签所代表的页面之间水平滑动,而列表则垂直滚动),则该TabBarView内部的任何列表都不会相互作用 与外部ScrollView。例如,浏览内部列表以滚动到顶部不会导致外部ScrollView中的SliverAppBar折叠以展开。

滚动隐藏AppBar

 _buildNestedScrollView(){
    return NestedScrollView(
        headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
          return [
            SliverAppBar(title: Text('导航测试'),)
          ];
        },
        body: ListView.builder(itemBuilder: (BuildContext context,int index){
          return Container(
            height: 120,
            color: Colors.primaries[index%Colors.primaries.length],
            alignment: Alignment.center,
            child: Text(
              '组合ListView $index',
              style: TextStyle(color: Colors.white,fontSize: 30),
            ),
          );
        }));
  }

效果:

上图 有个小bug。ListView和Appbar之间存在一个很大的间距,这个时候就需要MediaQuery去移除ListView的padding,才能取出间隔:代码如下:

_buildNestedScrollView(){
    return NestedScrollView(
        headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
          return [
            SliverAppBar(title: Text('导航测试'),)
          ];
        },
        body: MediaQuery.removePadding(
          removeTop: true,
            context: context,
            child: ListView.builder(itemBuilder: (BuildContext context,int index){
          return Container(
            height: 120,
            color: Colors.primaries[index%Colors.primaries.length],
            alignment: Alignment.center,
            child: Text(
              '组合ListView $index',
              style: TextStyle(color: Colors.white,fontSize: 30),
            ),
          );
        }))
    );
  }

效果如图:

 SliverAppBar展开折叠


_buildNestedScrollView1(){
    return NestedScrollView(
        headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
          return [
            SliverAppBar(
              expandedHeight: 230,
              pinned: true,
              flexibleSpace: FlexibleSpaceBar(
                title: Text('复仇者联盟'),
                background: Image.network(
                  'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
                  fit: BoxFit.fitHeight,
                ),
              ),
            )
          ];
        },
        body: ListView.builder(itemBuilder: (BuildContext context,int index){
          print('create $index');
          return Container(
            height: 100,
            color: Colors.primaries[index%Colors.primaries.length],
            alignment: Alignment.center,
            child: Text('$index 测试ListView',style: TextStyle(fontSize: 30),
            ),
          );
        }));
  }

效果:

 与TabBar配合使用


用法如下:

_buildNestedScrollViewTabBar() {
    return NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return [
            const SliverAppBar(
              expandedHeight: 230,
              pinned: true,
              flexibleSpace: Padding(
                padding: EdgeInsets.symmetric(vertical: 8),
                child: WyPageView(),
              ),

            ),
            SliverPersistentHeader(
              pinned: true,
              delegate: StickyTabBarDelegate(
                TabBar(
                  labelColor: Colors.black,
                  controller: this._tabController,
                  tabs: [
                    const Tab(text: '资讯',),
                    const Tab(text: '技术',),
                  ],
                ),
              ),
            )

          ];
        },
        body: TabBarView(
          controller: this._tabController,
            children: [
              RefreshIndicator(
                  child: _buildTabNewsList('----资讯类----'),
                  onRefresh: _handelRefresh,
              ),
              _buildTabNewsList('----技术类----'),
            ])
    );
  }
  //Refresh异步刷新方法
   Future<Null >_handelRefresh()async{
    print('加载数据');
    return null;
   }
   //构建newstlist列表
  _buildTabNewsList(String name) {
    return ListView.separated(itemBuilder: (context, int index) {
      return Column(
        children: [
          Text('$name $index 通过scrollDirection和reverse参数控制其滚动方向,用法如下:',
            style: TextStyle(fontSize: 18),),
          Text(
            '作者 csdn账号 ', style: TextStyle(fontSize: 12, color: Colors.grey),),
        ],
      );
    },
        separatorBuilder: (context, index) => Divider(),
        itemCount: 50);
  }
}
//StickyTabBarDelegate 代码如下:
class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
  final TabBar child;

  StickyTabBarDelegate(this.child);

  @override
  Widget build(BuildContext context, double shrinkOffset,
      bool overlapsContent) {
    // TODO: implement build
    return Container(
      color: Theme
          .of(context)
          .backgroundColor,
      child: this.child,
    );
  }

  @override
  // TODO: implement maxExtent
  double get maxExtent => this.child.preferredSize.height;

  @override
  // TODO: implement minExtent
  double get minExtent => this.child.preferredSize.height;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    // TODO: implement shouldRebuild
    throw true;
  }

}

 效果:

 controller为滚动控制器,可以监听滚到的位置,设置滚动的位置等,用法如下:

_scrollController = ScrollController();

//监听滚动位置
    _scrollController.addListener((){
      print('${_scrollController.position}');
    });
    //滚动到指定位置
    _scrollController.animateTo(20.0);

CustomScrollView(
  controller: _scrollController,
  ...
)

physics表示可滚动组件的物理滚动特性,系统提供的ScrollPhysics有:

  • AlwaysScrollableScrollPhysics:总是可以滑动

  • NeverScrollableScrollPhysics:禁止滚动

  • BouncingScrollPhysics :内容超过一屏 上拉有回弹效果

  • ClampingScrollPhysics :包裹内容 不会有回弹

源码地址:flutter_demo: flutter组件测试学习demo

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风雨「83」

你的鼓励将是我创作最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值