继续创造,加速生长!这是我参与「日新计划 10 月更文应战」的第19天,点击查看活动概况

概述:

Flutter中常用的滑动布局 ScrollViewSingleChildScrollViewNestedScrollViewCustomScrollView

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

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

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

如一个概况页面中 即需要 GridView 来完成二维宫格作用,也需要 ListView 列表作用,如下图所示的图片作用,当运用 CustomScrollView 结合 SliverList 和SliverGrid 就可轻松完成,当然结合一下 SliverAppBar 也能完成折叠作用的头部布局,所以说 CustomScrollView 很强壮。

在实践运用开发中,假如只是一个简略的适配页面滑动,主张码农运用 SingleChildScrollView 就能够。

如图:

CustomScrollView

CustomScrollView是运用Sliver组件创立自定义翻滚作用的翻滚组件,运用场景:

ListView和GridView相互嵌套场景,ListView嵌套GridView时,需要给GridView指定高度,但咱们期望高度随内容而改变(不指定),ListView和GridView作为全体翻滚作用。
一个页面顶部是AppBar,然后是GridView,最终是ListView,这3个区域以全体来翻滚,AppBar具有吸顶作用。

CustomScrollView就像一个粘合剂,将多个组件粘合在一起,具一致的翻滚作用。

Sliver系列组件有很多,比如SliverList、SliverGrid、SliverFixedExtentList、SliverPadding、SliverAppBar等。

相互嵌套场景

在实践事务场景中常常见到这样的布局,顶部是悬浮导航,接下来网格布局(GridView),然后是列表布局(ListView),翻滚的时分做为一个全体,此场景是无法运用GridView+ListView来完成的,而是需要运用CustomScrollView+SliverAppBar+SliverGrid+SliverList来完成,

CustomScrollView buildCustomScrollView() {
    return CustomScrollView(
      ///反弹作用
      physics: BouncingScrollPhysics(),
      ///Sliver 宗族的 Widget
      slivers: <Widget>[
        ///复杂的标题
        buildSliverAppBar(),
        ///距离
        SliverPadding(
          padding: EdgeInsets.all(5),
        ),
        ///九宫格
        buildSliverGrid(),
        ///距离
        SliverPadding(
          padding: EdgeInsets.all(5),
        ),
        ///列表
        buildSliverFixedExtentList()
      ],
    );
  }

SliverAppBar 常用来完成复杂的可折叠作用的头布局,代码如下:

SliverAppBar buildSliverAppBar() {
    return SliverAppBar(
      title: Text("解说组合滑动"),
    );
  }

CustomScrollView 中运用的九宫格你不能再去运用 GridView了,在Sliver宗族中,有SliverGridView,当然它与 GridView 的用法是一致的,代码如下:

 SliverGrid buildSliverGrid() {
    return SliverGrid(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        ///九宫格的列数
        crossAxisCount: 3,
        ///子Widget 宽与高的比值
        childAspectRatio: 2.0,
        ///主方向的 两个 子Widget 之间的距离
        mainAxisSpacing: 10,
        /// 次方向 子Widget 之间的距离
        crossAxisSpacing: 10,
      ),
      ///子Item构建器
      delegate: new SliverChildBuilderDelegate(
            (BuildContext context, num index) {
          ///每一个子Item的样式
          return Container(
            color: Colors.blue,
            child: Text("grid $index"),
          );
        },
        ///子Item的个数
        childCount: 10,
      ),
    );
  }

CustomScrollView 运用的列表你也不能运用 ListView了,需要运用SliverListView 或者是 SliverFixedExtentList,当然在这里运用了 SliverFixedExtentList 代码如下:

 SliverFixedExtentList buildSliverFixedExtentList() {
    return SliverFixedExtentList(
      ///子条目的高度
      itemExtent: 40,
      ///子条目布局构建代理
      delegate: new SliverChildBuilderDelegate(
            (BuildContext context, num index) {
          ///子条目的布局样式
          return Container(
            color: Colors.red,
            child: Text("list $index"),
            margin: EdgeInsets.only(bottom: 10),
          );
        },
        ///子条目的个数
        childCount: 40,
      ),
    );
  }

终究作用(上滑导航隐藏,下滑导航悬浮):

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

实践项目中页面顶部是AppBar,然后是GridView,最终是ListView,这3个区域以全体来翻滚,AppBar具有吸顶作用,此作用也是咱们常常遇到的,用法如下:

 CustomScrollView buildCustomScrollView1() {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          pinned: true,
          expandedHeight: 230.0,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('复仇者联盟'),
            background: Image.network(
              'https://www.6hu.cc/files/2022/12/1671191458-8c88005238f2717.jpg',
              fit: BoxFit.fitHeight,
            ),
          ),
        ),
        SliverGrid.count(crossAxisCount: 4,children: List.generate(8, (index){
          return Container(
            color: Colors.primaries[index%Colors.primaries.length],
            alignment: Alignment.center,
            child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
          );
        }).toList(),),
        SliverList(
          delegate: SliverChildBuilderDelegate((content, index) {
            return Container(
              height: 85,
              alignment: Alignment.center,
              color: Colors.primaries[index % Colors.primaries.length],
              child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
            );
          }, childCount: 25),
        )
      ],
    );
  }

运转作用:

经过scrollDirectionreverse参数操控其翻滚方向,用法如下:

CustomScrollView(
  scrollDirection: Axis.horizontal,
  reverse: true,
  ...
)

scrollDirection翻滚方向,分为笔直和水平方向。

reverse参数表明反转翻滚方向,并不是笔直转为水平,而是笔直方向翻滚时,默认向下翻滚,reverse设置false,翻滚方向改为向上,同理水平翻滚改为水平向左。

设置scrollDirection:Axis.horizontal作用:

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

primary设置为true时,不能设置controller,由于primarytrue时,controller运用PrimaryScrollController,这种机制带来的优点是父组件能够操控子树中可翻滚组件的翻滚行为,例如,Scaffold正是运用这种机制在iOS中完成了点击导航栏回到顶部的功用。

controller为翻滚操控器,能够监听滚到的方位,设置翻滚的方位等,用法如下:

_scrollController = ScrollController();
//监听翻滚方位
    _scrollController.addListener((){
      print('${_scrollController.position}');
    });
    //翻滚到指定方位
    _scrollController.animateTo(20.0);
CustomScrollView(
  controller: _scrollController,
  ...
)

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

physics表明可翻滚组件的物理翻滚特性,体系供给的ScrollPhysics有:

AlwaysScrollableScrollPhysics:总是能够滑动
NeverScrollableScrollPhysics:禁止翻滚
BouncingScrollPhysics :内容超越一屏 上拉有回弹作用
ClampingScrollPhysics :包裹内容 不会有回弹

NestedScrollView

能够在其内部嵌套其他翻滚视图的组件,其翻滚方位是固有链接的。

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

翻滚隐藏AppBar

代码如下:

_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),
            ),
          );
        }))
    );
  }

运转作用:

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

注意, 如不不加MediaQuery.removePadding移除顶部距离,有个小bug。ListView和Appbar之间存在一个很大的距离,这个时分就需要MediaQuery去移除ListView的padding。

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

SliverAppBar打开折叠

_buildNestedScrollView1(){
    return NestedScrollView(
        headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
          return [
            SliverAppBar(
              expandedHeight: 230,
              pinned: true,
              flexibleSpace: FlexibleSpaceBar(
                title: Text('复仇者联盟'),
                background: Image.network(
                  'https://www.6hu.cc/files/2022/12/1671191458-8c88005238f2717.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),
            ),
          );
        }));
  }

运转作用(白色距离能够增加removepadding去除):

19、 Flutter Widgets 之 粘合剂CustomScrollView,NestedScrollView滚动控件

与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;
  }
}

运转作用:

总结:

本篇首要介绍了CustomScrollViewNestedScrollView,CustomScrollView的自定义灵活性更大,一切子组件都用Sliver宗族组件,一般需求用NestedScrollView即可。