一,概述

列表是前端最常见的需求。 在flutter中,用ListView来显示列表页,支持垂直和水平方向展示,通过一个属性我们就可以控制其方向,列别有以下分类

  • 水平列表
  • 垂直列表
  • 数据量非常大的列表
  • 矩阵式的列表

二,构造函数
  构造方法有四种

  • new ListView

    • 解释
      默认构造函数采用子类的显式。此构造函数适用于具有少量(有限个)子项的列表视图,因为构造List需要为可能在列表视图中显示的每个子项执行工作,而不仅仅是那些实际可见的子项。
    • 构造函数
      ListView({
      Key key,
      Axis scrollDirection = Axis.vertical,
      bool reverse = false,
      ScrollController controller,
      bool primary,
      ScrollPhysics physics,
      bool shrinkWrap = false,
      EdgeInsetsGeometry padding,
      this.itemExtent,
      bool addAutomaticKeepAlives = true,
      bool addRepaintBoundaries = true,
      bool addSemanticIndexes = true,
      double cacheExtent,
      List<Widget> children = const <Widget>[],
      int semanticChildCount,
      }) : childrenDelegate = SliverChildListDelegate(
      children,
      addAutomaticKeepAlives: addAutomaticKeepAlives,
      addRepaintBoundaries: addRepaintBoundaries,
      addSemanticIndexes: addSemanticIndexes,
      ), super(
      key: key,
      scrollDirection: scrollDirection,
      reverse: reverse,
      controller: controller,
      primary: primary,
      physics: physics,
      shrinkWrap: shrinkWrap,
      padding: padding,
      cacheExtent: cacheExtent,
      semanticChildCount: semanticChildCount ?? children.length,
      );
    • 适用场景
      已知有限个Item的情况下

        

  • new ListView.builder
    • 解释
      它构造函数采用IndexedWidgetBuilder它根据需要构建子项。此构造函数适用于具有大量(或无限)子项数的列表视图,因为仅为实际可见的子项调用构建器。
    • 构造函数
       ListView.builder({
      Key key,
      Axis scrollDirection = Axis.vertical,//滚动方向,纵向或者横向
      bool reverse = false,//反转
      ScrollController controller,//
      bool primary,//
      ScrollPhysics physics,
      bool shrinkWrap = false,
      EdgeInsetsGeometry padding,
      this.itemExtent,
      @required IndexedWidgetBuilder itemBuilder,
      int itemCount,
      bool addAutomaticKeepAlives = true,
      bool addRepaintBoundaries = true,
      bool addSemanticIndexes = true,
      double cacheExtent,
      int semanticChildCount,
      }) : childrenDelegate = SliverChildBuilderDelegate(
      itemBuilder,
      childCount: itemCount,
      addAutomaticKeepAlives: addAutomaticKeepAlives,
      addRepaintBoundaries: addRepaintBoundaries,
      addSemanticIndexes: addSemanticIndexes,
      ), super(
      key: key,
      scrollDirection: scrollDirection,
      reverse: reverse,
      controller: controller,
      primary: primary,
      physics: physics,
      shrinkWrap: shrinkWrap,
      padding: padding,
      cacheExtent: cacheExtent,
      semanticChildCount: semanticChildCount ?? itemCount,
      );

       

    • 适用场景 
      长列表时采用builder模式,能提高性能。不是把所有子控件都构造出来,而是在控件viewport加上头尾的cacheExtent这个范围内的子Item才会被构造。在构造时传递一个builder,按需加载是一个惯用模式,能提高加载性能。
    • 示例代码
      List<Widget> _list = new List();
      for (int i = ; i < strItems.length; i++) {
      _list.add(buildListData(context, strItems[i], iconItems[i]));
      }
      // 添加分割线
      var divideList =
      ListTile.divideTiles(context: context, tiles: _list).toList();
      body: new Scrollbar(
      child: new ListView(
      // 添加ListView控件
      // children: _list, // 无分割线
      children: divideList, // 添加分割线
      ),
      );
  • new ListView.separated
    • 解释
      它的构造函数有两个IndexedWidgetBuilder 构建器: itemBuilder根据需要构建子项,separatorBuilder 类似地构建出现在子项之间的分隔子项。此构造函数适用于具有固定数量子项的列表视图。
    • 构造函数
      ListView.separated({
      Key key,
      Axis scrollDirection = Axis.vertical,
      bool reverse = false,
      ScrollController controller,
      bool primary,
      ScrollPhysics physics,
      bool shrinkWrap = false,
      EdgeInsetsGeometry padding,
      @required IndexedWidgetBuilder itemBuilder,
      @required IndexedWidgetBuilder separatorBuilder,
      @required int itemCount,
      bool addAutomaticKeepAlives = true,
      bool addRepaintBoundaries = true,
      bool addSemanticIndexes = true,
      double cacheExtent,
      }) : assert(itemBuilder != null),
      assert(separatorBuilder != null),
      assert(itemCount != null && itemCount >= ),
      itemExtent = null,
      childrenDelegate = SliverChildBuilderDelegate(
      (BuildContext context, int index) {
      final int itemIndex = index ~/ ;
      Widget widget;
      if (index.isEven) {
      widget = itemBuilder(context, itemIndex);
      } else {
      widget = separatorBuilder(context, itemIndex);
      assert(() {
      if (widget == null) {
      throw FlutterError('separatorBuilder cannot return null.');
      }
      return true;
      }());
      }
      return widget;
      },
      childCount: _computeSemanticChildCount(itemCount),
      addAutomaticKeepAlives: addAutomaticKeepAlives,
      addRepaintBoundaries: addRepaintBoundaries,
      addSemanticIndexes: addSemanticIndexes,
      semanticIndexCallback: (Widget _, int index) {
      return index.isEven ? index ~/ : null;
      }
      ), super(
      key: key,
      scrollDirection: scrollDirection,
      reverse: reverse,
      controller: controller,
      primary: primary,
      physics: physics,
      shrinkWrap: shrinkWrap,
      padding: padding,
      cacheExtent: cacheExtent,
      semanticChildCount: _computeSemanticChildCount(itemCount),
      );
    • 适用场景: 列表中需要分割线时,可以自定义复杂的分割线  
    • 示例代码:
      child: new ListView.separated(
      itemCount: iconItems.length,
      separatorBuilder: (BuildContext context, int index) => new Divider(), // 添加分割线
      itemBuilder: (context, item) {
      return buildListData(context, strItems[item], iconItems[item]);
      },
      ),
  • new ListView.custom
    • 解释
      构造需要SliverChildDelegate提供自定义子项的其他方面的能力。例如,SliverChildDelegate可以控制用于估计实际上不可见的子项大小的算法。
    • 构造函数
      const ListView.custom({
      Key key,
      Axis scrollDirection = Axis.vertical,
      bool reverse = false,
      ScrollController controller,
      bool primary,
      ScrollPhysics physics,
      bool shrinkWrap = false,
      EdgeInsetsGeometry padding,
      this.itemExtent,
      @required this.childrenDelegate,
      double cacheExtent,
      int semanticChildCount,
      }) : assert(childrenDelegate != null),
      super(
      key: key,
      scrollDirection: scrollDirection,
      reverse: reverse,
      controller: controller,
      primary: primary,
      physics: physics,
      shrinkWrap: shrinkWrap,
      padding: padding,
      cacheExtent: cacheExtent,
      semanticChildCount: semanticChildCount,
      );

        

    • 适用场景:上面几种模式基本可以满足业务需求,如果你还想做一些其它设置(如列表的最大滚动范围)或获取滑动时每次布局的子Item范围,可以尝试custom模式
    • 示例代码:
      List<Widget> _list = new List();
      
      @override
      Widget build(BuildContext context) {
      for (int i = ; i < strItems.length; i++) {
      _list.add(buildListData(context, strItems[i], iconItems[i]));
      }
      var divideList =
      ListTile.divideTiles(context: context, tiles: _list).toList();
      return new Scaffold(
      body: new Scrollbar(
      // 默认方式 List
      // child: new ListView(
      // children: divideList, //添加ListView控件
      // ),
      // ListView.separated 方式
      // child: new ListView.separated(
      // itemCount: iconItems.length,
      // separatorBuilder: (BuildContext context, int index) => new Divider(),
      // itemBuilder: (context, item) {
      // return buildListData(context, strItems[item], iconItems[item]);
      // },
      // ),
      // ListView.builder 方式
      child: new ListView.builder(
      itemCount: iconItems.length,
      itemBuilder: (context, item) {
      return new Container(
      child: new Column(
      children: <Widget>[
      buildListData(context, strItems[item], iconItems[item]),
      new Divider()
      ],
      ),
      );
      },
      ),
      // child: new ListView.custom(
      //
      // ),
      ),
      );
      }
  • ListTitle
    • Flutter 提供了一种常见的列表 item 样式,可以包括前后图标以及大小标题的样构造函数
    • const ListTile({
      Key key,
      this.leading, // item 前置图标
      this.title, // item 标题
      this.subtitle, // item 副标题
      this.trailing, // item 后置图标
      this.isThreeLine = false, // item 是否三行显示
      this.dense, // item 直观感受是整体大小
      this.contentPadding, // item 内容内边距
      this.enabled = true,
      this.onTap, // item onTap 点击事件
      this.onLongPress, // item onLongPress 长按事件
      this.selected = false, // item 是否选中状态
      })

三,参数解析

  • Key key

    • 解释:key值
  • Axis scrollDirection:Axis.vertical
    • 解释:ListView 的方向,为 Axis.vertical 表示纵向,为 Axis.horizontal 表示横向。
  • bool reverse:false
    • 解释:是否反向滚动
  • ScrollController controller
    • 解释:滑动监听,值为一个 ScrollController 对象,这个属性应该可以用来做下拉刷新和上垃加载
  • bool primary
    • 解释:是否是与父级PrimaryScrollController关联的主滚动视图。如果primary为true,controller必须设置
  • ScrollPhysics physics
    • 解释:
       设置 ListView 如何响应用户的滑动行为,值为一个 ScrollPhysics 对象,它的实现类常用的有:

      AlwaysScrollableScrollPhysics:总是可以滑动。
          NeverScrollableScrollPhysics:禁止滚动。
          BouncingScrollPhysics:内容超过一屏,上拉有回弹效果。
          ClampingScrollPhysics:包裹内容,不会有回弹,感觉跟 AlwaysScrollableScrollPhysics 差不多。

  • bool shrinkWrap:false 
    • 解释:多用于嵌套listView中 内容大小不确定 比如 垂直布局中 先后放入文字 listView (需要Expend包裹否则无法显示无穷大高度 但是需要确定listview高度 shrinkWrap使用内容适配不会有这样的影响)
  • EdgeInsetsGeometry padding
    • 解释:滚动视图与子项之间的内边距
  • double itemExtent
    • 解释:子项范围
  • required indexedWidgetBuilder
    • 解释:itemBuilder 位置构建器
  • int itemCount
    • 解释:子 Item 数量,只有使用 new ListView.builder() 和 new ListView.separated() 构造方法的时候才能指定,其中 new ListView.separated() 是必须指定。
  • bool addAutomaticKeepAlives:true
  • bool addRepaintBoundaries:true
  • bool addSemanticIndexes:true
  • double cacheExtent
  • child 
    • 解释:高度会适配 item填充的内容的高度,我们非常的不希望child的高度固定,因为这样的话,如果里面的内容超出就会造成布局的溢出。
  • int   semanticChildCount

四,示例demo
  

import 'package:flutter/material.dart';

//void main() => runApp(ListViewDemo());

class BaseBean {
String name;
int age;
String content; BaseBean(this.name, this.age, this.content);
} List<BaseBean> list = new List<BaseBean>.generate(
, (i) => new BaseBean("name$i", i, "content=$i")); class ListViewDemo extends StatefulWidget {
@override
_ListViewDemoState createState() => new _ListViewDemoState();
} class _ListViewDemoState extends State<ListViewDemo> {
List<BaseBean> list;
@override
void initState() {
// TODO: implement initState
super.initState();
list = new List<BaseBean>.generate(
, (i) => new BaseBean("name$i", i, "content=$i"));
} @override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text("ListView"),
),
body:
// listViewDefault(list))
listViewListTile(list))
// listViewLayoutBuilder(list)),
// listViewLayoutCustom(list)),
// listViewLayoutSeparated(list)),
);
} ///默认构建
Widget listViewDefault(List<BaseBean> list) {
List<Widget> _list = new List();
for (int i = ; i < list.length; i++) {
_list.add(new Center(
child: new Text(list[i].age.toString()),
));
} // 添加分割线
var divideList =
ListTile.divideTiles(context: context, tiles: _list).toList();
return new Scrollbar(
child: new ListView(
// 添加ListView控件
children: _list, // 无分割线
// children: divideList, // 添加分割线/
),
);
} /// ListTile
Widget listViewListTile(List<BaseBean> list) {
List<Widget> _list = new List();
for (int i = ; i < list.length; i++) {
_list.add(new Center(
child: ListTile(
leading: new Icon(Icons.list),
title: new Text(list[i].name),
trailing: new Icon(Icons.keyboard_arrow_right),
),
));
}
return new ListView(
children: _list,
);
} ///listView builder 构建
Widget listViewLayoutBuilder(List<BaseBean> list) {
return ListView.builder(
//设置滑动方向 Axis.horizontal 水平 默认 Axis.vertical 垂直
scrollDirection: Axis.vertical,
//内间距
padding: EdgeInsets.all(10.0),
//是否倒序显示 默认正序 false 倒序true
reverse: false,
//false,如果内容不足,则用户无法滚动 而如果[primary]为true,它们总是可以尝试滚动。
primary: true,
//确定每一个item的高度 会让item加载更加高效
itemExtent: 50.0,
//item 高度会适配 item填充的内容的高度 多用于嵌套listView中 内容大小不确定 比如 垂直布局中 先后放入文字 listView (需要Expend包裹否则无法显示无穷大高度 但是需要确定listview高度 shrinkWrap使用内容适配不会) 文字
shrinkWrap: true,
//item 数量
itemCount: list.length,
//滑动类型设置
//new AlwaysScrollableScrollPhysics() 总是可以滑动 NeverScrollableScrollPhysics禁止滚动 BouncingScrollPhysics 内容超过一屏 上拉有回弹效果 ClampingScrollPhysics 包裹内容 不会有回弹
// cacheExtent: 30.0, //cacheExtent 设置预加载的区域 cacheExtent 强制设置为了 0.0,从而关闭了“预加载”
physics: new ClampingScrollPhysics(),
//滑动监听
// controller ,
itemBuilder: (context, i) => new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text(
"${list[i].name}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
new Text(
"${list[i].age}",
style: new TextStyle(fontSize: 18.0, color: Colors.green),
),
new Text(
"${list[i].content}",
style: new TextStyle(fontSize: 18.0, color: Colors.blue),
),
],
),
));
} // ignore: slash_for_doc_comments
/**
* ListView({
List<Widget> children,
}) ListView.builder({
int: itemCount,
IndexedWidgetBuilder itemBuilder,
}) ListView.custom({
SliverChildDelegate childrenDelegate,
}) 大家可能对前两种比较熟悉,分别是传入一个子元素列表或是传入一个根据索引创建子元素的函数。
其实前两种方式都是第三种方式的“快捷方式”。因为 ListView 内部是靠这个 childrenDelegate 属性动态初始化子元素的。
*/ ///listView custom 构建
Widget listViewLayoutCustom(list) {
// return ListView.custom(childrenDelegate: new MyChildrenDelegate());
return ListView.custom(
itemExtent: 40.0,
childrenDelegate: MyChildrenDelegate(
(BuildContext context, int i) {
return new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text(
"${list[i].name}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
new Text(
"${list[i].age}",
style: new TextStyle(fontSize: 18.0, color: Colors.green),
),
new Text(
"${list[i].content}",
style: new TextStyle(fontSize: 18.0, color: Colors.blue),
),
],
));
},
childCount: list.length,
),
cacheExtent: 0.0,
);
}
} // ignore: slash_for_doc_comments
/**
* 继承SliverChildBuilderDelegate 可以对列表的监听
*/
class MyChildrenDelegate extends SliverChildBuilderDelegate {
MyChildrenDelegate(
Widget Function(BuildContext, int) builder, {
int childCount,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
}) : super(builder,
childCount: childCount,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries); ///监听 在可见的列表中 显示的第一个位置和最后一个位置
@override
void didFinishLayout(int firstIndex, int lastIndex) {
print('firstIndex: $firstIndex, lastIndex: $lastIndex');
} ///可不重写 重写不能为null 默认是true 添加进来的实例与之前的实例是否相同 相同返回true 反之false
///listView 暂时没有看到应用场景 源码中使用在 SliverFillViewport 中
@override
bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
// TODO: implement shouldRebuild
print("oldDelegate$oldDelegate");
return super.shouldRebuild(oldDelegate);
}
} /// listView separated 构建 用于多类型 分割
Widget listViewLayoutSeparated(List<BaseBean> list) {
return ListView.separated(
itemCount: list.length,
separatorBuilder: (content, index) {
//和itemBuilder 同级别的执行
if (index == ) {
return new Container(
height: 40.0,
child: new Center(
child: new Text("类型1"),
),
color: Colors.red,
);
} else if (index == ) {
return new Container(
height: 40.0,
child: new Center(
child: new Text("类型2"),
),
color: Colors.blue,
);
} else if (index == ) {
return new Container(
height: 40.0,
child: new Center(
child: new Text("类型3"),
),
color: Colors.yellow,
);
} else {
return new Container();
}
},
itemBuilder: (content, i) {
return new InkWell(
child: new Container(
height: 30.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text(
"${list[i].name}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
new Text(
"${list[i].age}",
style: new TextStyle(fontSize: 18.0, color: Colors.green),
),
new Text(
"${list[i].content}",
style: new TextStyle(fontSize: 18.0, color: Colors.blue),
),
],
)),
onTap: () {
print("");
},
);
// return ;
},
);
}

五,官方文档
  官方文档

【Flutter学习】基本组件之基本列表ListView组件的更多相关文章

  1. Flutter学习笔记(12)--列表组件

    如需转载,请注明出处:Flutter学习笔记(12)--列表组件 在日常的产品项目需求中,经常会有列表展示类的需求,在Android中常用的做法是收集数据源,然后创建列表适配器Adapter,将数据源 ...

  2. Flutter学习笔记(15)--MaterialApp应用组件及routes路由详解

    如需转载,请注明出处:Flutter学习笔记(15)--MaterialApp应用组件及routes路由详解 最近一段时间生病了,整天往医院跑,也没状态学东西了,现在是好了不少了,也该继续学习啦!!! ...

  3. Flutter学习笔记(18)--Drawer抽屉组件

    如需转载,请注明出处:Flutter学习笔记(18)--Drawer抽屉组件 Drawer(抽屉组件)可以实现类似抽屉拉出和推入的效果,可以从侧边栏拉出导航面板.通常Drawer是和ListView组 ...

  4. Flutter学习笔记(24)--SingleChildScrollView滚动组件

    如需转载,请注明出处:Flutter学习笔记(23)--多 在我们实际的项目开发中,经常会遇到页面UI内容过多,导致手机一屏展示不完的情况出现,以Android为例,在Android中遇到这类情况的做 ...

  5. 【Flutter学习】页面布局之列表和表格处理

    一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...

  6. 【Flutter学习】页面布局之基础布局组件

    一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...

  7. Flutter学习笔记(13)--表单组件

    如需转载,请注明出处:Flutter学习笔记(13)--表单组件 表单组件是个包含表单元素的区域,表单元素允许用户输入内容,比如:文本区域,下拉表单,单选框.复选框等,常见的应用场景有:登陆.注册.输 ...

  8. Flutter学习笔记(25)--ListView实现上拉刷新下拉加载

    如需转载,请注明出处:Flutter学习笔记(25)--ListView实现上拉刷新下拉加载 前面我们有写过ListView的使用:Flutter学习笔记(12)--列表组件,当列表的数据非常多时,需 ...

  9. Flutter学习笔记(11)--文本组件、图标及按钮组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 文本组件 文本组件(text)负责显示文本和定义显示样式,下表为text常见属性 Text组件属性及描述 属性名 类型 默认 ...

随机推荐

  1. C常量

    C 常量 常量是固定值,在程序执行期间不会改变.这些固定的值,又叫做字面量. 常量可以是任何的基本数据类型,比如整数常量.浮点常量.字符常量,或字符串字面值,也有枚举常量. 常量就像是常规的变量,只不 ...

  2. PHP chdir() 函数

    实例 改变当前的目录: <?php// Get current directoryecho getcwd() . "<br>"; // Change direct ...

  3. UNP学习 ioctl操作

    一.ioctl函数 #include <unistd.h> int ioctl(int fd, int request, ... /* void * arg */); 返回:成功0,出错- ...

  4. Database基础(四):密码恢复及设置、 用户授权及撤销、数据备份与恢复、MySQL管理工具

    一.密码恢复及设置 目标: 本案例要求熟悉MySQL管理密码的控制,完成以下任务操作: 练习重置MySQL管理密码的操作 通过正常途径设置MySQL数据库的管理密码 步骤: 步骤一:重置MySQL管理 ...

  5. MySQL strcmp 函数

    STRCMP(str1, str2) 比较两个字符串,如果这两个字符串相等返回0,如果第一个参数是根据当前的排序小于第二个参数顺序返回-1,否则返回1. mysql> SELECT STRCMP ...

  6. HTML5: HTML5 Audio(音频)

    ylbtech-HTML5: HTML5 Audio(音频) 1.返回顶部 1. HTML5 Audio(音频) HTML5 提供了播放音频文件的标准. 互联网上的音频 直到现在,仍然不存在一项旨在网 ...

  7. vue消息提示Message

    https://www.iviewui.com/components/message this.$Message.info(config) this.$Message.success(config) ...

  8. Oracle之Group by和Having-----转了

    在介绍GROUP BY 和 HAVING 子句前,我们必需先讲讲sql语言中一种特殊的函数:聚合函数,例如SUM, COUNT, MAX, AVG等.这些函数和其它函数的根本区别就是它们一般作用在多条 ...

  9. 25. Postman的使用

    Postman下载与安装 不管是接口测试人员还是开发人员大概率下都绕不开一个工具,那就是Postman.当然可能还有一些接口测试工具,比如soapUI.Jmeter.Robot Framework 等 ...

  10. Codeforces 578B "Or" Game (前缀和 + 贪心)

    Codeforces Round #320 (Div. 1) [Bayan Thanks-Round] 题目链接:B. "Or" Game You are given \(n\) ...