列表布局在手机上是最最常用的控件了。
Android上的RecycleView尤其的强大。
Flutter中也给我们提供了ListView,不过就目前的体验来看,性能跟原生的ListView或RecycleView比还是有一点差距的。滑动起来还是略卡。
下面我们来看一下ListView的相关知识
可以看到,ListView和GridView都继承自BoxScrollView,因此,他们的属性差不多,用法也相似。
构造方法
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 children = const [],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
})
常用属性
属性 | 值类型 | 说明 |
---|---|---|
scrollDirection | Axis | 设置滚动的方向,horizontal(水平)或vertical(垂直) |
reverse | bool | 是否翻转 |
itemExtent | double | 滚动方向子控件的长度,垂直方向即为高度,水平方向即为宽度 |
controller | ScrollController | 用来控制滚动位置及监听滚动事件 |
shrinkWrap | bool | 是否根据子widget的总长度来设置ListView的长度 |
padding | EdgeInsetsGeometry | 间距 |
children | List | 子控件 |
常用属性都比较简单,没啥好说的,我们来直接用一下
ListView(
children: List.generate(30, (index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: Text("item${index}"),
);
}),
);
这里我们要注意一下itemExtent属性
itemExtent
该属性用来控制子控件在滚动方向上的长度,所以它跟scrollDirection是密切相关的。
例如:
当滚动方向为垂直方向时,那么itemExtent就是控制子控件的高度
当滚动方向为水平方向时,那么itemExtent就是控制子控件的宽度
示例:我们在垂直方向上设置一下itemExtent
ListView(
scrollDirection: Axis.vertical,
itemExtent: 60,
children: List.generate(50, (index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: Text("item${index}"),
);
}),
);
我们更改一下滚动方向为水平:
ListView(
scrollDirection: Axis.horizontal,
itemExtent: 60,
children: List.generate(50, (index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: Text("item${index}"),
);
}),
);
构造方法适合在数据较少且固定的情况下使用,我们在开发过程中一般数据都是从网络获取的,并且基本都有分页的需求,此时,我们就需要动态的加载数据了。
跟GridView一样,ListView提供了ListView.builder方法供我们动态加载数据时使用,且使用方法基本一致。
首先看看源码:
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,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
})
属性跟构造方法中的属性差不多,我们主要来看看itemCount和itemBuilder
itemCount表示列表的数量,一般都是集合的长度
itemBuilder是列表项构造器,返回一个Widget
这里我就直接用了
import 'package:flutter/material.dart';
/*
* 列表控件
*
* */
class ListViewWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.horizontal,
itemExtent: 60,
children: List.generate(50, (index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: Text("item${index}"),
);
}),
);
}
}
class ListViewBuilder extends StatefulWidget {
@override
State createState() {
return _ListViewBuilder();
}
}
class _ListViewBuilder extends State {
/*初始项为50个*/
List indexs = List.generate(50, (index) {
return index;
});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: indexs.length,
itemBuilder: (context, index) {
/*加载到底部时且集合数量小于100的话,获取更多数据*/
if (index==indexs.length-1&&indexs.length < 100) {
_getMoreData(index);
}
return Text("${index}");
});
}
void _getMoreData(int index) {
Future.delayed(Duration(milliseconds: 300)).then((e) {
setState(() {
/*往集合里添加10条数据*/
indexs.addAll(List.generate(10, (i) {
return index + i;
}));
});
});
}
}
通常情况下,我们的列表之间都有分割线,便于用户区分,我们既可以在子控件中手动添加分割线的样式,例如一开始的构造方法那样,但是那样会造成分割线重叠的情况。
而ListView.separated可以让我们方便的给列表加上分割线。
我们先来看一下源码:
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,
})
可以看到属性中多了一个separatorBuilder
separatorBuilder即为分割线构建器
下面我们给上面的列表加上灰色分割线:
import 'package:flutter/material.dart';
/*
* 列表控件
*
* */
class ListViewWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.horizontal,
itemExtent: 60,
children: List.generate(50, (index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: Text("item${index}"),
);
}),
);
}
}
class ListViewBuilder extends StatefulWidget {
@override
State createState() {
return _ListViewBuilder();
}
}
class _ListViewBuilder extends State {
/*初始项为50个*/
List indexs = List.generate(50, (index) {
return index;
});
@override
Widget build(BuildContext context) {
/*灰色分割线*/
var divider = Divider(
color: Colors.grey,
);
return ListView.separated(
padding: EdgeInsets.all(10),
itemCount: indexs.length,
separatorBuilder: (context, index) {
return divider;
},
itemBuilder: (context, index) {
/*加载到底部时且集合数量小于100的话,获取更多数据*/
if (index == indexs.length - 1 && indexs.length < 100) {
_getMoreData(index);
}
return Text("${index}");
});
}
void _getMoreData(int index) {
Future.delayed(Duration(milliseconds: 300)).then((e) {
setState(() {
/*往集合里添加10条数据*/
indexs.addAll(List.generate(10, (i) {
return index + i;
}));
});
});
}
}
好了 Flutter ListView的基本用法大概就是这样
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可。也可以关注我的 Flutter 博客专栏,我会不定期的更新,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!