Flutter Widgets粘合剂CustomScrollView NestedScrollView滚动控件

目录
  • 概述:
  • CustomScrollView
    • 相互嵌套场景
  • NestedScrollView
    • 滚动隐藏AppBar
    • SliverAppBar展开折叠
    • 与TabBar配合使用
  • 总结:

概述:

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

最终效果(上滑导航隐藏,下滑导航悬浮):

实际项目中页面顶部是AppBar,然后是GridView,最后是ListView,这3个区域以整体来滚动,AppBar具有吸顶效果,此效果也是我们经常遇到的,用法如下:

 CustomScrollView buildCustomScrollView1() {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          pinned: true,
          expandedHeight: 230.0,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('复仇者联盟'),
            background: Image.network(
              'http://img.haote.com/upload/20180918/2018091815372344164.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效果:

primary设置为true时,不能设置controller,因为primarytrue时,controller使用PrimaryScrollController,这种机制带来的好处是父组件可以控制子树中可滚动组件的滚动行为,例如,Scaffold正是使用这种机制在iOS中实现了点击导航栏回到顶部的功能。

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

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

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

运行效果:

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

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

运行效果(白色间距可以增加removepadding去除):

与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即可。

以上就是Flutter Widgets粘合剂CustomScrollView NestedScrollView滚动控件的详细内容,更多关于Flutter Widgets滚动控件的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter 首页必用组件NestedScrollView的示例详解

    昨天Flutter 1.17版本重磅发布,新的版本主要是优化性能.修复bug,有人觉得此版本毫无亮点,但也从另一方面体现了Flutter目前针对移动端已经较为完善,想了解具体内容,文末有链接,如果你想升级到最新版本,建议慎重,有些人升级后项目无法运行. 今天介绍的组件是NestedScrollView,大部分的App首页都会用到这个组件. 可以在其内部嵌套其他滚动视图的滚动视图,其滚动位置是固有链接的. 在普通的ScrollView中, 如果有一个Sliver组件容纳了一个TabBarView,

  • NestedScrollView+Recyclerview下滑卡顿解决方法

    大家在进行安卓开发用到NestedScrollView+Recyclerview的时候,经常出现的情况就是加载下滑的时候没有任何问题,很流畅,但是在下滑以后明显出现了卡顿的情况,小编根绝这个问题,给大家再来的解决方法,一起来学习下. 我们先来看下这个BUG的表现: 1.滑动卡顿, 2.加载下滑时流畅,下滑时明显的卡顿 3.进入页面时直接加载RecyclerView部分的内容(这里我理解为控件惯性,不知道对不对-------尴尬!!!!!!) 下面我们一一来解决这些问题 在开发项目中,涉及到到商品

  • Android使用NestedScrollView 内嵌RecycleView滑动冲突问题解决

    目录 场景描述 实现思路 问题和优化 优化 场景描述 使用NestedScrollView 内嵌RecycleView时,当用户上滑时,NestedScrollView需要首先响应上滑事件,直到ScrollView无法滑动,再由RecycleView进行响应滑动事件 效果演示: 实现思路 参考谷歌开发者文档中关于view group事件分发的思路,自定义CustomScrollView继承 NestedScrollView 重写onInterceptTouchEvent方法,在拦截到上滑事件时,

  • Android使用RecyclerView实现水平滚动控件

    前言 相信大家都知道Android滚动控件的实现方式有很多, 使用RecyclerView也比较简单. 做了一个简单的年龄滚动控件, 让我们来看看RecyclerView的使用方式, 主要有以下几点: (1) 对齐控件中心位置. (2) 计算滚动距离. (3) 高亮中心视图. (4) 实时显示中心数据. (5) 停止时自动对齐. (6) 滚动时, 设置按钮状态开关. 效果 1. 框架 主要关注RecyclerView部分逻辑. /** * 初始化年龄滑动条 */ private void ini

  • Android垂直滚动控件ScrollView使用方法详解

    一.简介 二.方法 1)ScrollView垂直滚动控件使用方法 1.在layout布局文件的最外层建立一个ScrollView控件 2.在ScrollView控件中加入一个LinearLayout控件,并且把它的orientation设置为vertical 3.在LinearLayout控件中放入多个装有图片的ImageView控件 三.代码实例 1.效果图 2.代码 /Ex27ScrollView/src/fry/Activity01.java <?xml version="1.0&q

  • HorizontalScrollView水平滚动控件使用方法详解

    一.简介 用法ScrollView大致相同 二.方法 1)HorizontalScrollView水平滚动控件使用方法 1.在layout布局文件的最外层建立一个HorizontalScrollView控件 2.在HorizontalScrollView控件中加入一个LinearLayout控件,并且把它的orientation设置为horizontal 3.在LinearLayout控件中放入多个装有图片的ImageView控件 2)HorizontalScrollView和ScrollVie

  • 浅谈Android RecyclerView UI的滚动控件示例

    ListView 由于其强大的功能,在过去的 Andorid 开发中使用非常广泛.不过 ListView 需要优化来提升运行效率,就像我们之前所优化的那样,否则性能将很差.还有就是只能够纵向滚动,如果要想实现横向移动,用 ListView 是做不到的. RecyclerView 可以说是一个增强版的 ListView .它不仅实现了和 ListView 同样的效果,而且还优化了 ListView 存在的各种不足. RecyclerView 现在可是官方推荐使用的滚动控件哦O(∩_∩)O~ 1 基

  • Android高级图片滚动控件实现3D版图片轮播器

    大家好,好久不见了,最近由于工作特别繁忙,已经有一个多月的时间没写博客了,我也是深感惭愧.那么今天的这篇既然是阔别了一个多月的文章,当然要带来更加给力点的内容了,那么话不多说,赶快进入到今天的正题吧. 说到图片轮播器,很多的Android应用中都会带有这个功能,比如说网易新闻.淘宝等.最新我们公司的一款应用也加入了这个功能,并且在图片轮播的基础上还增加了三维立体的效果,但比较遗憾的是,整体效果并不理想,用户体验性比较糟糕.因此,我就花了点时间去编写了一个效果更好的3D图片轮播器,自我感觉还是比较

  • android水平循环滚动控件使用详解

    本文实例为大家分享了android水平循环滚动控件的具体代码,供大家参考,具体内容如下 CycleScrollView.java package com.example.test; import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector;

  • 功能强大的Android滚动控件RecyclerView

    RecyclerView的使用比ListView的使用是比较复杂的,ListView的使用是五个步骤,而我们的RecyclerView的使用有7个步骤,分别为: 1.在当前项目的build.gradle中的dependencies闭包中加入compile 'com.android.support.recyclerview-v7:xx.x.x'(x是当前最新版本) 2.布局加入RecyclerView控件以及创建子项布局和适配器类. 3.创建适配器 4.定义数据源 5.通过findViewById

  • Android自定义view实现滚动选择控件详解

    目录 前言 需求 编写代码 主要问题 前言 上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec.onMeasure以及onLayout,接下来就用一个滚动选择的控件(View)来学一下onDraw的使用,并且了解下在XML自定义控件参数. 需求 这里就是一个滚动选择文字的控件,还是挺常见的,之前用别人的,现在选择手撕一个,核心思想如下: 1.有三层不同大小及透明度的选项,选中项放在中间 2.接受一个列表的数据,静态时显示三个值,滚动时显示四个

  • Android UI 中的 ListView列表控件的示例

    当程序中有大量的数据需要展示时,就需要用到 ListView 啦.ListView 允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕. 1 基本用法 布局文件中加入 ListView: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/and

  • Android UI控件之ListView实现圆角效果

    今天在Android群里面有人再求圆角ListView的实现方式,正好自己以前实现过.因此就共享了现在将其实现方式写在博客中共他人学习.给出实现方式之前顺带加点自己的想法,感觉上android中方形的ListView还是太"硬性",没有圆角的有亲和力.连Apple也为了"圆角"这个设计去申请专利. 看来圆角确实比较适合现在人们的喜好吧. 照老规矩先上两张效果图吧: 第一张: 第二张: 该方式主要就是需要重新去实现自己的ListView代码如下: package co

随机推荐