如今,几乎每个平凡的移动应用都可能在其布局中包含列表。 那是因为使用可滚动列表通常是在小屏幕上显示大量相似项目的最直接方法。
Flutter框架提供了几个小部件,可用于以最少的代码有效地创建和显示此类列表。 在本教程中,我将向您展示如何将它们与本地和远程数据源一起使用。
1.显示不可滚动列表
如果您需要显示少量相似的项目,并且可以确保用户的屏幕能够同时容纳所有这些项目,那么使用Column
小部件是最有效的方法。
要在Flutter应用中创建列表,可以使用Dart编程语言提供的List
类。 创建列表后,可以使用其add()
方法向其中添加任意数量的项目。 以下代码显示了如何创建包含三个RaisedButton
小部件的列表:
// Create list
List<RaisedButton> myItems = new List();
// Add three button widgets to it
myItems.add(new RaisedButton(
child: new Text("Twitter"),
onPressed: (){}
));
myItems.add(new RaisedButton(
child: new Text("Facebook"),
onPressed: (){}
));
myItems.add(new RaisedButton(
child: new Text("Reddit"),
onPressed: (){}
));
请注意,列表中的每个项目都有一个与之关联的空onPressed
事件处理程序,因为如果不这样做,该项目将被禁用。
现在,该列表已准备就绪,您可以将其直接分配给要显示的Column
小部件的children
属性。 但是,通常,您通常还需要指定列表项在屏幕上的放置位置。 由于Column
小部件是flex小部件,因此可以使用mainAxisAlignment
和crossAxisAlignment
属性控制项目沿其主轴和十字轴的mainAxisAlignment
。 默认情况下,对于“ Column
窗口小部件,主轴是垂直轴,而交叉轴是水平轴。
下面的代码向您展示如何在水平轴上拉伸三个按钮并将它们垂直放置在屏幕中央:
Column column = new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: myItems
);
这是该列现在的样子:
重要的是要注意,如果Column
小部件无法容纳其所有子级,则会遇到运行时错误。 例如,如果列表中有十二个以上的RaisedButton
小部件,而不是三个,那么您将看到一条错误消息,如下所示:
2.显示一个简单的滚动列表
对于稍大的列表,其内容可能会在屏幕上溢出的列表,必须考虑使用ListView
小部件,因为它支持滚动。
您可能已经注意到,我们在上一步中编写的用于创建列表的代码既冗长又重复。 使用相同的方法创建更大的列表可能非常繁琐。 一种简单的替代方法是改为使用两个列表:一个包含数据,一个包含小部件。
这是您可以使用[]
运算符快速创建数据列表的方法,该列表目前仅包含几个字符串:
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
"Tumblr", "Instagram", "Pinterest"];
要将上面的字符串列表转换为RaisedButton
小部件列表,可以使用map()
和toList()
方法。 使用map()
方法,您可以使用每个字符串来生成一个新的RaisedButton
小部件。 使用toList()
方法,可以将map()
方法返回的Iterable
对象转换为实际的List
对象。 以下代码向您展示了如何:
List<RaisedButton> myWidgets = data.map((item) {
return new RaisedButton(
child: new Text(item),
onPressed: () async {
String url = "https://${item}.com";
if(await canLaunch(url))
await launch(url);
}
);
}).toList();
为了完整起见,上面的代码还向您展示了如何创建一个onPressed
事件处理程序,该事件处理程序使用url_launcher
软件包提供的canLaunch()
和launch()
方法来打开用户在默认浏览器中选择的网站。
列表准备好后,可以将其传递到ListView
小部件的children
属性以显示它。
ListView myList = new ListView(
children: myWidgets
);
此时,如果您运行该应用程序,则应该可以滚动列表并按任意按钮以启动关联的网站。
3.创建一个网格
ListView
小部件仅允许您在其横轴上放置一项。 默认情况下,该项目将被拉伸以占据该轴上的所有可用空间。 如果需要更大的灵活性,则应考虑使用GridView
小部件,该小部件可让您指定横轴上需要多少个项目。
以下代码使用GridView.count()
构造函数创建一个GridView
小部件,该小部件每行显示两个项目:
GridView myGrid = GridView.count(
crossAxisCount: 2,
children: myWidgets
);
网格如下所示:
4.显示大清单
对于包含几十个项目的数据列表,应避免像先前步骤那样手动生成窗口小部件列表。 为什么? 因为创建窗口小部件是一项昂贵的操作,并且大量窗口小部件会占用大量内存。
相反,您应该使用IndexedWidgetBuilder
函数,该函数仅在用户需要查看它们时才使您生成它们。 有了它,您可以在用户滚动浏览ListView
小部件时懒惰地生成小部件。
您不太可能在应用程序内部直接定义大量数据。 通常,您将从远程服务器获取此类数据。 因此,为您提供一个现实的示例,让我现在向您展示如何使用Stack Exchange API从Stack Overflow提取100个问题并按需显示它们。
首先创建StatefulWidget
类的子类,该子类将充当ListView
小部件的容器并覆盖其createState()
方法。
class VeryLargeListHolder extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyState();
}
}
上面代码中提到的MyState
类尚不存在,因此请创建它并覆盖其build()
方法。
class MyState extends State<VeryLargeListHolder> {
@override
Widget build(BuildContext context) {
// TODO
}
}
接下来,添加一个List
对象作为类的成员变量之一。 您将使用它来存储从Stack Overflow下载的问题。 此外,将API的端点添加为另一个变量。
List questions;
String endpoint = "https://api.stackexchange.com/2.2/questions?" +
"pagesize=100&order=desc&sort=activity&site=stackoverflow";
除非您希望用户按下按钮来下载问题,否则建议您在小部件初始化时自动下载问题。 因此,重写initState()
方法并调用一个名为loadData()
的新异步方法。
@override
void initState() {
super.initState();
loadData();
}
void loadData() async {
// More code here
}
在loadData()
方法内部,可以使用Dart的http
程序包的get()
函数下载问题。 API端点返回一个JSON文档,您可以使用Dart的convert
包中提供的json.decode()
函数进行解析。 以下代码向您展示了如何:
String rawData = (await http.get(endpoint)).body;
Map jsonData = json.decode(rawData);
将JSON文档转换为Map
对象后,您可以使用与其items
键关联的值来初始化questions
变量。 但是,变量是窗口小部件状态的一部分。 因此,必须确保仅在setState()
方法内对其进行更新。 这是如何做:
setState(() {
questions = jsonData["items"];
});
此时,您可以使用ListView.builder()
构造函数创建一个新的ListView
小部件,该IndexedWidgetBuilder
函数需要一个IndexedWidgetBuilder
函数和一个项目计数作为其参数。 目前,项目数不过是questions
列表的大小。 因此,在MyState
类的build()
方法内添加以下代码:
ListView myList = ListView.builder(
itemCount: questions == null ? 0 : questions.length,
itemBuilder: (BuildContext context, int index) {
// More code here
}
);
在构建器功能内,您所需要做的就是创建一个小窗口小部件树,以显示有关您下载的每个问题的各种详细信息。 Flutter的material
包提供了一个非常方便的小部件ListTile
,它使您可以在遵循“材料设计”准则的同时快速创建这样的树。
以下代码显示了如何使用ListTile
小部件的title
和subtitle
属性显示问题的标题和作者:
return new ListTile(
title: Text(questions[index]["title"]),
subtitle: Text("Asked by ${questions[index]["owner"]["display_name"]}")
);
最后,创建一个新的Scaffold
小部件,将ListView
小部件分配给它的body
属性,然后从build()
方法返回它,以便它可以与MaterialApp
小部件一起使用。 (可选)您可以将AppBar
小部件添加到Scaffold
小部件。
return new Scaffold(
appBar: new AppBar(
title: new Text("LargeListDemo")
),
body: myList
);
在下载问题后,应用程序的外观如下所示:
结论
您现在知道了如何在Flutter应用程序中使用列表。 在本教程中,您不仅学习了如何创建支持大型数据源的列表和网格,还学习了如何使其中的每个项目都具有交互性。 要了解有关Flutter中列表的更多信息,可以参考官方文档 。