Flutter 列表組件
列表布局是我們項(xiàng)目開(kāi)發(fā)中最常用的一種布局方式脱盲。Flutter 中我們可以通過(guò) ListView 來(lái)定義 列表項(xiàng)广匙,支持垂直和水平方向展示弧可。通過(guò)一個(gè)屬性就可以控制列表的顯示方向嘁字。列表有一下 分類:
1.垂直列表
2.垂直圖文列表
3.水平列表
4.動(dòng)態(tài)列表
5.矩陣式列表
構(gòu)造方法
方法一
默認(rèn)構(gòu)造函數(shù)采用子類的顯式夫椭。此構(gòu)造函數(shù)適用于具有少量(有限個(gè))子項(xiàng)的列表視圖掸掸,因?yàn)闃?gòu)造List需要為可能在列表視圖中顯示的每個(gè)子項(xiàng)執(zhí)行工作,而不僅僅是那些實(shí)際可見(jiàn)的子項(xiàng)
class ListView extends BoxScrollView {
/// Creates a scrollable, linear array of widgets from an explicit [List].
///
/// This constructor is appropriate for list views with a small number of
/// children because constructing the [List] requires doing work for every
/// child that could possibly be displayed in the list view instead of just
/// those children that are actually visible.
///
/// It is usually more efficient to create children on demand using [new
/// ListView.builder].
///
/// The `addAutomaticKeepAlives` argument corresponds to the
/// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
/// `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property. The
/// `addSemanticIndexes` argument corresponds to the
/// [SliverChildListDelegate.addSemanticIndexes] property. None
/// may be null.
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,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : 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,
dragStartBehavior: dragStartBehavior,
);
方法二
它構(gòu)造函數(shù)采用IndexedWidgetBuilder它根據(jù)需要構(gòu)建子項(xiàng)蹭秋。此構(gòu)造函數(shù)適用于具有大量(或無(wú)限)子項(xiàng)數(shù)的列表視圖扰付,因?yàn)閮H為實(shí)際可見(jiàn)的子項(xiàng)調(diào)用構(gòu)建器。
長(zhǎng)列表時(shí)采用builder模式仁讨,能提高性能羽莺。不是把所有子控件都構(gòu)造出來(lái),而是在控件viewport加上頭尾的cacheExtent這個(gè)范圍內(nèi)的子Item才會(huì)被構(gòu)造洞豁。在構(gòu)造時(shí)傳遞一個(gè)builder盐固,按需加載是一個(gè)慣用模式,能提高加載性能丈挟。
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.start,
}) : 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,
dragStartBehavior: dragStartBehavior,
);
方式三
它的構(gòu)造函數(shù)有兩個(gè)IndexedWidgetBuilder 構(gòu)建器: itemBuilder
根據(jù)需要構(gòu)建子項(xiàng)刁卜,separatorBuilder
類似地構(gòu)建出現(xiàn)在子項(xiàng)之間的分隔子項(xiàng)。此構(gòu)造函數(shù)適用于具有固定數(shù)量子項(xiàng)的列表視圖曙咽。
列表中需要分割線時(shí)蛔趴,可以自定義復(fù)雜的分割線
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 >= 0),
itemExtent = null,
childrenDelegate = SliverChildBuilderDelegate(
(BuildContext context, int index) {
final int itemIndex = index ~/ 2;
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 ~/ 2 : null;
},
),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: _computeSemanticChildCount(itemCount),
);
方式四
構(gòu)造需要SliverChildDelegate提供自定義子項(xiàng)的其他方面的能力。例如桐绒,SliverChildDelegate可以控制用于估計(jì)實(shí)際上不可見(jiàn)的子項(xiàng)大小的算法夺脾。
上面幾種模式基本可以滿足業(yè)務(wù)需求,如果你還想做一些其它設(shè)置(如列表的最大滾動(dòng)范圍)或獲取滑動(dòng)時(shí)每次布局的子Item范圍茉继,可以嘗試custom模式
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,
);
常用屬性
屬性名 | 功能 | 值所屬類型 |
---|---|---|
children | 列表元素 | List<Widget> |
scrollDirection | Axis.horizontal 水平列表Axis.vertical 垂直列表 | Axis |
padding | 內(nèi)邊距 | EdgeInsetsGeometry |
resolve | 組件反向排序 | bool |
基本使用
/*
*listView 的基本使用方法
*/
class MyListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
),
ListTile(
leading: Icon(Icons.phone),
title: Text(
"你好數(shù)據(jù)的交換機(jī)",
style: TextStyle(fontSize: 28.0),
),
subtitle: Text(
"listview listview",
style: TextStyle(fontSize: 18.0),
),
)
],
),
);
}
}
ListView.separated帶有下劃線的ListView
/**
* 帶有下劃線的listview
*/
class MyListView2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.separated(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
if (index.isOdd) {
return new Container(
padding: new EdgeInsets.all(15.0),
child: new Text(
"builder 奇數(shù) Item " + index.toString(),
style:
new TextStyle(fontSize: 20.0, color: new Color(0xFFFF0000)),
),
);
} else {
return new Container(
padding: new EdgeInsets.all(15.0),
child: new Text(
"builder 偶數(shù) Item " + index.toString(),
style:
new TextStyle(fontSize: 20.0, color: new Color(0xFF0000FF)),
),
);
}
},
separatorBuilder: (BuildContext context, int index) {
return new Divider(
color: new Color(0xFF888888),
);
},
);
}
}
ListView.builder 的用法
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 100, //多少數(shù)據(jù)
itemBuilder: (BuildContext context, int index) {
if (index.isOdd) {
return new Container(
padding: new EdgeInsets.all(15.0),
child: new Text(
"builder 奇數(shù) Item " + index.toString(),
style:
new TextStyle(fontSize: 20.0, color: new Color(0xFFFF0000)),
),
);
} else {
return new Container(
padding: new EdgeInsets.all(15.0),
child: new Text(
"builder 偶數(shù) Item " + index.toString(),
style:
new TextStyle(fontSize: 20.0, color: new Color(0xFF0000FF)),
),
);
}
},
);
}
}
ListView.custom 的用法
class MyListView1 extends StatefulWidget {
@override
_MyListViewState createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView1> {
List<String> items = <String>['1', '2', '3', '4', '5'];
void _reverse() {
setState(() {
items = items.reversed.toList();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView.custom(
childrenDelegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return KeepAlive(
data: items[index],
key: ValueKey<String>(items[index]),
);
},
childCount: items.length,
findChildIndexCallback: (Key key) {
final ValueKey valueKey = key;
final String data = valueKey.value;
return items.indexOf(data);
}),
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () => _reverse(),
child: Text('Reverse items'),
),
],
),
),
);
}
}
網(wǎng)絡(luò)請(qǐng)求
/*
* 請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)
*/
class MyListView3 extends StatelessWidget {
List<Widget> _getListData() {
var tempList = listData.map((value) {
return ListTile(
title: Text(value["title"]),
leading: Image.network(value["imageUrl"],fit: BoxFit.cover,));
// return Image(
// image: NetworkImage(value["imageUrl"]),
// width: 300.0,
// height: 200.0,
// fit: BoxFit.cover,
// );
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
return ListView(children: this._getListData());
}
}