美文网首页Flutter知识库
Flutter第十章(Scrollable ,Scrollba

Flutter第十章(Scrollable ,Scrollba

作者: 一巴掌拍出两坨脂肪 | 来源:发表于2021-04-27 14:05 被阅读0次
    版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!

    情感语录: 如果你是对的,你没必要发脾气;如果你是错的,你没资格去发脾气。这才是真正的智慧。

    欢迎来到本章节,上一章节我们讲了常用表单的使用,知识点回顾 戳这里 Flutter基础第九章

    本章节主要讲解可以滚动的组件,很多时候一个页面内容会相当多;当组件内容超过当前显示视图范围时,如果没有特殊处理,Flutter 则会提示Overflow错误;在第二章中我们有讲到 ListViewGridView 可以实现滚动效果,利用这两个组件可以规避这类问题。但是有些复杂的界面交互效果,使用它们可能就变得非常不友好了。假如有一个页面,顶部需要一个GridView,底部需要一个ListView,而要求整个页面的滑动效果是统一的,即它们看起来是一个整体。此时如果使用GridView+ListView来实现的话,就不能保证一致的滑动效果,因为它们的滚动效果是分离的,所以这时就需要一个"胶水",把这些彼此独立的可滚动组件"粘"起来。

    本章简要:

    1、Scrollable 组件

    2、Scrollbar 、CupertinoScrollbar 组件

    3、SingleChildScrollView、 CustomScrollView 组件

    4、SliverList、SliverFixedExtentList、 SliverGrid 组件

    5、SliverPadding 、SliverAppBar 组件

    6、ScrollController 滚动监听和控制

    一、Scrollable 组件

    可滚动组件都直接或间接包含一个Scrollable 组件,比如我们之前学的ListViewGridView 组件,而他们的父类 BoxScrollView 是继承与ScrollView实现的,而 ScrollView中就包含了 Scrollable 组件。 所以可滚动类组件包括一些共同的属性:

    Scrollable构造函数:

        Scrollable({
          ...// 去除部分属性
          this.axisDirection = AxisDirection.down,
          this.controller,
          this.physics,
          @required this.viewportBuilder, //进阶篇讲
        })
    

    axisDirection : 滚动方向。

    physics :此属性接受一个ScrollPhysics类型的对象,Flutter会根据具体平台分别使用不同的ScrollPhysics对象,应用不同的显示效果:

      ClampingScrollPhysics:Android下微光效果。
    
      BouncingScrollPhysics:iOS下弹性效果。
    

    controller:此属性接受一个ScrollController对象。ScrollController的主要作用是控制滚动位置和监听滚动事件。

    二、Scrollbar 、 CupertinoScrollbar 组件

    Scrollbar是一个Material 风格的滚动条,如果要给可滚动组件添加滚动条,只需将 Scrollbar作为可滚动组件的任意一个父级组件即可,如:

        Scrollbar(
          child: SingleChildScrollView(
            ...
          ),
        );
    

    CupertinoScrollbar 是iOS风格的滚动条,如果你使用的是Scrollbar,那么在iOS平台它会自动切换为CupertinoScrollbar。

    三、SingleChildScrollView、 CustomScrollView 组件

    1、SingleChildScrollView 组件

    SingleChildScrollView类似于Android中的 ScrollView,它只能接收一个子组件。它是继承 StatelessWidget 实现的,并非 继承 ScrollView。需要注意的是,通常SingleChildScrollView 只应在期望的内容不会超过屏幕太多时使用,因为SingleChildScrollView不支持基于Sliver的延迟实例化模型,所以如果预计视图可能包含超出屏幕尺寸太多的内容时,那么使用SingleChildScrollView将会 非常耗性能,此时应该使用一些支持Sliver延迟加载的可滚动组件,如: ListView。

    构造函数:

      const SingleChildScrollView({
        Key key,
        this.scrollDirection = Axis.vertical,
        this.reverse = false,
        this.padding,
        bool primary,
        this.physics,
        this.controller,
        this.child,
        this.dragStartBehavior = DragStartBehavior.start,
      })
    

    除了上面的通用属性外,它下面的两个属性也非常有用:

    reverse:该属性表示是否反向,就是说初始滚动位置是在“头”还是“尾”,取false时,初始滚动位置在“头”,反之则在“尾”,默认为false。

    primary:指是否使用widget树中默认的 PrimaryScrollController;当滑动方向为垂直方向(scrollDirection值为Axis.vertical)并且没有指定controller时,primary默认为true.

    简单运用:

      import 'package:flutter/material.dart';
    
      class ScrollerViewPage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
              debugShowCheckedModeBanner: false,
              home: Scaffold(
                  appBar: AppBar(
                    title: Text("ScrollerViewPage"),
                  ),
                  body: Container(
                      child: SingleChildScrollView(
                          reverse: false,
                          child: SingleChildScrollViewDemo()))));
        }
      }
    
      class SingleChildScrollViewDemo extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          List<Widget> listWidget = new List();
    
          for (int i = 0; i < 40; i++) {
            var text = Text("Item$i",
                style: TextStyle(fontSize: 16, color: Colors.redAccent));
            listWidget.add(text);
            listWidget.add(Divider());
          }
          return Column(children: listWidget);
        }
      }
    

    效果如下:

    SingleChildScrollView.gif

    从效果可以看出 SingleChildScrollView 实现了 ListView 的滚动效果,但是需要注意的时 SingleChildScrollView 包裹的内容不能太多,否则引起性能问题。

    2、CustomScrollView 组件

    默认场景下,Scalfold 的导航栏都是固定写死的,如果要做一些交互性或者是沉浸式的交互比较困难。Flutter 提供了 CustomScrollView 来帮助实现跟随列表滑动发生一些变化的 AppBar 效果。CustomScrollView 继承自 ScrollView,它可以完成 SingleChildScrollView 能完成的工作,且 CustomScrollView 是可滚动模型。

    构造函数:

      const CustomScrollView({
        Key key,
        Axis scrollDirection = Axis.vertical,
        bool reverse = false,
        ScrollController controller,
        bool primary,
        ScrollPhysics physics,
        bool shrinkWrap = false,
        Key center,
        double anchor = 0.0,
        double cacheExtent,
        this.slivers = const <Widget>[],
        int semanticChildCount,
        DragStartBehavior dragStartBehavior = DragStartBehavior.start,
      })
    

    除了上面讲过的几个属性外,下面来看几个其他重要的属性。

    shrinkWrap 配置可控制 AppBar 下的内容在滚动时,是否可以超过 AppBar 的边界。如果为true,则滑动内容区域 可覆盖 AppBar 直到屏幕顶端。如果 为 false ,则只能在 AppBar 以下区域滑动,即 AppBar 始终在顶部显示。

    anchor 该属性不太好描述,它的的取值范围值0.0-1.0之间,这个属性迫使组件将其自身定位在父组件中的某个相对或绝对位置。后面演示观察下。

    slivers List<Widget> 类型,用来承载滑动内容

    四、 SliverList、SliverFixedExtentList、 SliverGrid 组件

    1、 这三个组件你可以理解是 Sliver版 (ListView(SliverList、SliverFixedExtentList)、GridView(SliverGrid))组件。SliverList 就是一个 ListView,只不过在整个实现上,需要指定一个 delegate,比如通过 SliverChildBuilderDelegate 进行列表的构建。

       SliverList(
          delegate: new SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                //创建列表项
                return new Container(
                  alignment: Alignment.center,
                  child: new Text('list item $index'),
                );
              },
              childCount: 50 //50个列表项
          ),
        ),
    

    SliverFixedExtentList 你可以看做是 SliverList的加强版,它可以使用 itemExtent 属性来控制 item 的范围(高度)。

    2、SliverGrid 如同 GridView 一样,只不过在整个实现上,需要指定一个 delegate 和 gridDelegate,比如通过 SliverChildBuilderDelegate 进行列表的构建,SliverGridDelegateWithFixedCrossAxisCount 来控制列数和行间距和列间距

      SliverPadding(
              padding:  EdgeInsets.all(8.0),
              sliver:  SliverGrid( //Grid
                gridDelegate:  SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2, //Grid按两列显示
                  mainAxisSpacing: 10.0,
                  crossAxisSpacing: 10.0,
                  childAspectRatio: 4.0,
                ),
                delegate: new SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    //创建子widget
                    return new Container(
                      alignment: Alignment.center,
                      color: Colors.cyan[100 * (index % 9)],
                      child: new Text('grid item $index'),
                    );
                  },
                  childCount: 20,
                ),
              ),
            ),
    

    五、SliverPadding 、SliverAppBar 组件

    1、SliverPadding 组件同 前面讲的 Padding 组件是一样的,只不过 SliverPadding 接收的子组件是 sliver Widget。

      const SliverPadding({
        Key key,
        @required this.padding,
        Widget sliver,
      })
    

    2、SliverAppBar 组件 是专门服务于 CustomScrollView 的; SliverAppBar 支持的属性和 AppBar 支持的属性基本无异,SliverAppBar 因为需要跟随 ScrollView 的一些操作,属性会多一些。

    构造函数:

      const SliverAppBar({
        Key key,
        this.leading,
        this.automaticallyImplyLeading = true,
        this.title,
        this.actions,
        this.flexibleSpace,
        this.bottom,
        this.elevation,
        this.forceElevated = false,
        this.backgroundColor,
        this.brightness,
        this.iconTheme,
        this.actionsIconTheme,
        this.textTheme,
        this.primary = true,
        this.centerTitle,
        this.titleSpacing = NavigationToolbar.kMiddleSpacing,
        this.expandedHeight,
        this.floating = false,
        this.pinned = false,
        this.snap = false,
        this.shape,
      }) 
    

    除开和AppBar 中属性外,下面来看几个重要属性:

    floating: false 表示当列表往下滑动时,会先将列表内容滚动到顶部,然后再将 SliverAppBar 浮动出现,true表示当列表往下滑动时,会先将 SliverAppBar 浮动出现(与列表是否滚动到顶部无关),然后再继续列表的滑动。

    pinned 属性能够决定是否将导航栏部分固定。true 表示导航栏不会完全消失,否则随滚动逐渐消失。

    expandedHeight 属性可以配置AppBar 展开后的高度。

    flexibleSpace 属性可以定制 AppBar 展开后的样式,结合 FlexibleSpaceBar 控制。

    下面来综合运用下,这几个新组建:

      import 'package:flutter/material.dart';
    
      class ScrollerViewPage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
    
          return CustomScrollViewDemo();
        }
      }
    
      class CustomScrollViewDemo extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return Material(
            color: Colors.green,
            child: CustomScrollView(
              //距离 AppBar 百分之10 高度
              anchor: 0.1,
              slivers: <Widget>[
                //AppBar,包含一个导航栏
                SliverAppBar(
                  pinned: true,
                  expandedHeight: 200.0,
                  flexibleSpace: FlexibleSpaceBar(
                    title: Text('ScrollerViewPage'),
                   //centerTitle: true,
                    background: Image.asset(
                      "images/mm.jpg",
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
    
                SliverPadding(
                  padding: EdgeInsets.all(8.0),
                  sliver: SliverGrid(
                    //Grid
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2, //Grid按两列显示
                      mainAxisSpacing: 10.0,
                      crossAxisSpacing: 10.0,
                      childAspectRatio: 4.0,
                    ),
                    delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                        //创建子widget
                        return Container(
                          alignment: Alignment.center,
                          color: Colors.red[100 * ((index + 2) % 9)],
                          child: Text('grid item $index'),
                        );
                      },
                      childCount: 20,
                    ),
                  ),
                ),
                //List
                SliverFixedExtentList(
                  itemExtent: 50.0,
                  delegate: new SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    //创建列表项
                    return Container(
                      alignment: Alignment.center,
                      color: Colors.lightBlue[100 * ((index + 2) % 9)],
                      child: Text('list item $index'),
                    );
                  }, childCount: 50 //50个列表项
                      ),
                ),
              ],
            ),
          );
        }
      }
    

    效果如下:

    综合运用.gif

    可以看出 在设置 anchor 属性后 底部的 SliverGrid 和 SliverAppBar 之间产生了整个父容器的百分之10距离,且上滑动时 SliverGrid 滚动超过这段距离后才会把 SliverAppBar 往上顶。下滑时 SliverAppBar 完全展开后才逐渐显示这段距离。

    六、ScrollController 滚动监听和控制

    本章和前面章节讲解的 ListView 、 GridView;都是可滚动组件,在开发中很多时候需要监听 这些可滚动组件的滚动位置信息,比如,滚动到某一个距离后要显示一个视图,再或者需要从某一位置需要跳到另一个位置时等。在 Flutter 中 ScrollController 能帮助我们解决这类问题。

    构造函数:

      ScrollController({
        double initialScrollOffset = 0.0,
        this.keepScrollOffset = true,
        this.debugLabel,
      }) 
    

    ScrollController 中常用的属性和方法:

    initialScrollOffset : 初始滚动位置。

    keepScrollOffset : 是否保存滚动位置。

    addListener() : 添加滚动位置信息监听。

    removeListener() : 删除滚动位置信息监听。

    offset: 返回滑动距离的像素单位值。

    dispose(): 销毁 ScrollController 控制器。

    jumpTo(): 跳转到某一位置,不带动画。

    animateTo(): 跳转到某一位置,带动画效果,Curves 给我们提供了 几十种动画效果可供选择。

    下面来将上面的例子进行改造下,我需要监听 CustomScrollView 的滑动信息,当滑动距离超过500个像素后 我要在屏幕上显示一个回到顶部的按钮,然后点击按钮直接回到顶部去。这里面设计到界面模板的刷新 需要继承 StatefulWidget 来改造,且需要结合前面学的 FloatingActionButton 组件去实现(如果忘了,请回顾前面知识点)

      import 'package:flutter/material.dart';
    
      class ScrollerViewPage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
    
          return CustomScrollViewDemo();
        }
      }
    
    
    
      class CustomScrollViewDemo extends StatelessWidget {
    
        @override
        Widget build(BuildContext context) {
          return Material(
              child: ScrollListenerDemo()
          );
        }
      }
    
      class ScrollListenerDemo extends StatefulWidget {
        @override
        _ScrollListenerDemoState createState() => _ScrollListenerDemoState();
      }
    
      class _ScrollListenerDemoState extends State<ScrollListenerDemo> {
    
        ScrollController _controller = new ScrollController();
        //是否显示“返回到顶部”按钮
        bool showToTopBtn = true;
    
        @override
        void initState() {
          //监听滚动事件,打印滚动位置
          _controller.addListener(() {
            print(_controller.offset); //打印滚动位置
            if (_controller.offset < 500 && showToTopBtn) {
              setState(() {
                showToTopBtn = false;
              });
            } else if (_controller.offset >= 500 && showToTopBtn == false) {
              setState(() {
                showToTopBtn = true;
              });
            }
          });
        }
    
        @override
        void dispose() {
          //为了避免内存泄露,需要调用_controller.dispose
          _controller.dispose();
          super.dispose();
        }
    
        @override
        Widget build(BuildContext context) {
          return Scaffold(
    
              floatingActionButton: !showToTopBtn ? null : FloatingActionButton(
                backgroundColor: Colors.deepPurple,
                  child: Icon(Icons.arrow_upward),
                  onPressed: () {
                    //返回到顶部时执行动画
                    _controller.animateTo(0.0,
                        duration: Duration(milliseconds: 1000),
                        curve: Curves.easeInBack
                    );
                  }
              ),
    
            body: CustomScrollView(
              controller: _controller,
              slivers: <Widget>[
                //AppBar,包含一个导航栏
                SliverAppBar(
                  pinned: true,
                  expandedHeight: 200.0,
                  flexibleSpace: FlexibleSpaceBar(
                    title: Text('ScrollerViewPage'),
                    //centerTitle: true,
                    background: Image.asset(
                      "images/mm.jpg",
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
    
                SliverPadding(
                  padding: EdgeInsets.all(8.0),
                  sliver: SliverGrid(
                    //Grid
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 2, //Grid按两列显示
                      mainAxisSpacing: 10.0,
                      crossAxisSpacing: 10.0,
                      childAspectRatio: 4.0,
                    ),
                    delegate: SliverChildBuilderDelegate(
                          (BuildContext context, int index) {
                        //创建子widget
                        return Container(
                          alignment: Alignment.center,
                          color: Colors.red[100 * ((index + 2) % 9)],
                          child: Text('grid item $index'),
                        );
                      },
                      childCount: 20,
                    ),
                  ),
                ),
                //List
                SliverFixedExtentList(
                  itemExtent: 50.0,
                  delegate: new SliverChildBuilderDelegate(
                          (BuildContext context, int index) {
                        //创建列表项
                        return Container(
                          alignment: Alignment.center,
                          color: Colors.lightBlue[100 * ((index + 2) % 9)],
                          child: Text('list item $index'),
                        );
                      }, childCount: 50 //50个列表项
                  ),
                ),
              ],
            )
          );
        }
      }
    

    效果如下:

    ScrollController监听控制.gif

    本章节的东西不多,但是值得去研究的内容还有很多,更多高级用法还需自己去探索。

    好了本章节到此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星,你们的支持才是创作的动力,如有错误,请热心的你留言指正, 谢谢大家观看,下章再会 O(∩_∩)O

    实例源码地址: https://github.com/zhengzaihong/flutter_learn

    相关文章

      网友评论

        本文标题:Flutter第十章(Scrollable ,Scrollba

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