Flutter控件——常用控件:ListView

Posted wzj_what_why_how

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter控件——常用控件:ListView相关的知识,希望对你有一定的参考价值。

ListView

scroll_view.dart 源码

ListView(
    Key? key,
    Axis scrollDirection = Axis.vertical,    // 滚动方向:横/纵(默认)
    bool reverse = false,    // 决定滚动方向是否与阅读方向一致,true 表示不一样,显示时是列表直接滚动到最底下,最后一个 item 显示第一个数据
    ScrollController? controller,    //  主要作用是控制滚动位置和监听滚动事件

    // primary: 当内容不足以滚动时,是否支持滚动;对于ios系统还有一个效果:当用户点击状态栏时是否滑动到顶部。这个挺重要的,
    //尤其是使用列表构建页面时配合下拉刷新,要是数据量小于屏幕高度的话,这个就管用了。此时系统会自动给 listview 设置
    //一个默认的滚动控制器  PrimaryScrollController,好处是父组件可以控制子树中可滚动组件的滚动行为
    bool? primary,
    ScrollPhysics? physics,    //
    bool shrinkWrap = false,    //  是否根据子组件的总长度来设置 ListView 的长度,默认值为 false,所以能滚动。滚动组件相互嵌套时,shrinkWrap 属性要设置 true 才行,和 NeverScrollableScrollPhysics 配合就能解决滚动冲突
    EdgeInsetsGeometry? padding,    //
    this.itemExtent,    //  可以直接设置列表项高度,可以提高列表性能
    this.prototypeItem,    //

    //addAutomaticKeepAlives - 该属性表示是否将列表项(子组件)包裹在AutomaticKeepAlive组件中,在一个懒加载列表中,
    //如果将列表项包裹在AutomaticKeepAlive中,在该列表项滑出视口时也不会被回收,它会使用KeepAliveNotification来保
    //存其状态。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,    //
    bool addSemanticIndexes = true,    //
    double? cacheExtent,    //
    List<Widget> children = const <Widget>[],    //
    int? semanticChildCount,    //
    DragStartBehavior dragStartBehavior = DragStartBehavior.start,    //
    ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,    //
    String? restorationId,    //
    Clip clipBehavior = Clip.hardEdge,    //
  )

四种构建方式

4种构建方式,也就是有4个构造方法。核心思路就是把生成 item 的部分看成一个对象函数(各种build) 传进构造方法中。
build 设计模式,都是包装成 build。

第一种:静态列表。把固定数据变成 item 传给 childen (ps:一般要将ListView抽出来一个类去放,然后再使用,酱紫不会导致代码视觉上嵌套太多层)

   ListView(
     padding: EdgeInsets.all(20),
      children: <Widget>[
        Text("AA"),
        Text("BB"),
        Text("CC"),
      ],
   );

   ListView(
        padding: EdgeInsets.all(20),
         children: <Widget>[
            ListTile(
               leading: Icon(Icons.ac_unit),
               title: Text("这个列表子项"),
             ),
             ListTile(
               leading: Icon(Icons.ac_unit),
               title: Text("这个列表子项"),
             ),
             ListTile(
               leading: Icon(Icons.ac_unit),
               title: Text("这个列表子项"),
             ),
         ],
   );

   ListView(
          Image.network(
                'https://profile.csdnimg.cn/8/9/E/0_wzj_what_why_how',
              width: 200,
              height: 200,
            ),
            Container(
              width: 200,
              height: 200,
              color: Colors.purple,
            ),
            Image.network(
              'https://profile.csdnimg.cn/8/9/E/0_wzj_what_why_how',
              width: 200,
              height: 200,
            ),
            ],
   );

第二种:动态列表 ListView.builder()

List类型的使用

List是Dart的集合类型之一,其实你可以把它简单理解为数组(反正我是这么认为的),其他语言也都有这个类型。它的声明有几种方式:
    var myList = List(): 非固定长度的声明。
    var myList = List(2): 固定长度的声明。
    var myList= List<String>():固定类型的声明方式。
    var myList = [1,2,3]: 对List直接赋值。

eg: 构建50个子项的List:

//generate方法传递两个参数,第一个参数是生成的个数,第二个是方法。
items = List<String>.generate(50, (index) => "这是选项:$index")

ListView.builder - 构建一种 item 类型的列表 (在 itemBuilder 使用 if、slse 也是能支持多类型 item 的)

new ListView.builder(
    itemCount: items.length, //列表数量
    itemBuilder: (context, index)
      return new ListTile(title: new Text('$items[index]')); //子项控件

)

第三种: ListView.separated - 带分割线的列表 (separatorBuilder:在这个参数中构建分割线)

  ListView.separated(
      padding: EdgeInsets.all(20),
      itemCount: 50,
      itemBuilder: (context, index) 
        return Text("item:$index");
      ,
      //扩展:可以根据 index 找到前后 item 的类型,然后考虑可以采取不同类型的分隔 item
      separatorBuilder: (context,index)
        return Container(
          height: 1,
          color: Colors.pink,
        );
      ,
    );
  

第四种:ListView.custom()

需要传入一个实现了 SliverChildDelegate 的组件,如 SliverChildListDelegate 和 SliverChildBuilderDelegate。
SliverChildListDelegate 就是 listview,SliverChildBuilderDelegate 就是 ListView.builder。
这个 ListView.custom 用的也比较少。

参数–scrollController 滚动控制 : 主要功能就是监听滚动状态,提供方法滚动到列表指定位置

可以设置选择列表从哪里开始显示,配合 itemExtent 每列固定高度设置是个不错的思路。

scroll_controller.dart源码:

 ScrollController(
    double initialScrollOffset = 0.0,//初始滚动位置
    this.keepScrollOffset = true, //是否保存滚动位置
    this.debugLabel,
  )

scrollController 的核心参数、方法:

  • offset - 当前滚动到的位置,注意这个数据是累计值,不是每次滚动的量
  • jumpTo(double offset) - 滚动到指定位置,不带动画。目前只能滚动到指定 px
  • animateTo(double offset,…) - 滚动到指定位置,带动画,可以指定时间

scrollController 可以添加多个 Listener 呢的,从其方法 scrollController.addListener 就能看的出来,走进源码中 _listeners 是一个集合类型

NotificationListener 滚动监听

android 中有 NestedScrollingParent、NestedScrollingChild 这一对接口,NestedScrollingChild 发送滚动事件,NestedScrollingParent 控制滚动事件的传播和数值。
Flutter 继承这一思路。在 widget 树中,可滚动 widget 滚动时会逐次向上传递滚动事件 notification,我们可以通过NotificationListener 可以监控到该 notification,NotificationListener 也是一个 widget,只要NotificationListener 的布局层级比 listview 高就行,隔几层都没关系,一样可以监听的到。

只要是 Flutter 中的滚动 widget,如:ScrollView、ListView、PageView 等都能使用 NotificationListener 监听滚动事件。

监听方法:
NotificationListener 中 onNotification(ScrollNotification notification) 方法可以拿到滚动事件,数值包裹在 ScrollNotification 这个参数中。需要我们返回一个 boolean 值:

true - 那么 notification 到此为止,我们拦截了滚动事件,不会继续上传滚动事件,但是不影响 listview 自身 widget 的滚动
false - 那 么notification 会继续向更外层 widget 传递。

……

ListTile

内部列表。不仅可以使用在ListView组件中,然后容器组件其实都可以使用。

参考:Flutter - Listview 详解:https://juejin.cn/post/6844904019475824648

以上是关于Flutter控件——常用控件:ListView的主要内容,如果未能解决你的问题,请参考以下文章

Flutter控件篇(Stateful widget)——ListView

flutter控件之ListView滚动布局

Flutter控件——常用控件:Icon

Flutter控件——常用控件:Icon

Flutter初探--常用依赖包

Android最常用的控件ListView(详解)