Flutter控件篇(Stateful widget)——ListView
Posted Bliss91
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter控件篇(Stateful widget)——ListView相关的知识,希望对你有一定的参考价值。
使用 package开源软件包
我们开始使用一个名为 english_words 的开源软件包,其中包含数千个最常用的英文单词以及一些实用功能。
你可以 在 pub.dartlang.org 上找到 english_words 软件包以及其他许多开源软件包。pubspec 文件管理
pubspec 文件管理 Flutter 应用程序的 assets(资源,如图片、package等)。 在pubspec.yaml 中,将 english_words(3.1.0或更高版本)添加到依赖项列表,如下面注释显示的行,要注意对齐方式
1dependencies:
2 flutter:
3 sdk: flutter
4
5 # The following adds the Cupertino Icons font to your application.
6 # Use with the CupertinoIcons class for ios style icons.
7 cupertino_icons: ^0.1.0
8 english_words: ^3.1.0 # 新增了这一行,此处有格式限定,最好和上边对齐
在android Studio 的编辑器视图中查看 pubspec 时,单击右上角的 Packages get,这会将依赖包安装到您的项目。您可以在控制台中看到以下内容:
如果安装未能成功,就要检查你添加依赖时的对齐格式,IDE会自动帮你判定对齐格式如下图标注所示:
在 lib/main.dart 中引入更改后的依赖
.import 'package:english_words/english_words.dart'; // 新增了这一行
依赖引入
在您输入时,Android Studio会为您提供有关库导入的建议。然后它将呈现灰色的导入字符串,让您知道导入的库截至目前尚未被使用。
使用 English words 包生成文本来替换字符串
1class MyApp extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 final wordPair = new WordPair.random(); //新增了这一行
5 return new MaterialApp(
6 title: 'Welcome to Flutter',
7 home: new Scaffold(// 使用 Scaffold 类实现基础的 Material Design 布局
8 appBar: new AppBar(
9 title: const Text('Welcome to Flutter'),
10 ),
11// body: const Center(//conset指代常量
12// child: const Text('Hello World'),我们不用这样的方式生成文字了
13// ),
14 body: new Center(
15 // 这里把之前的 "const" 换成了 "new".
16 child: new Text(wordPair.asPascalCase), // 这是新的文字生成方式
17
18 ),
19 ),
20 );
21 }
22}
如果你没有把 Center 前面的修饰词从 const 改成 new 的话,系统就会报错,因为这个时候它的子对象已经不是常量,那就不能再用 const 了,所以这里 Center 和 Text 都需要使用 new 创建新的实例。
提示:「大驼峰式命名法」也称为 upper camel case 或 Pascal case,表示字符串中的每个单词(包括第一个单词)都以大写字母开头。所以,uppercamelcase 会变成 UpperCamelCase。
如果应用程序正在运行,请使用热重载按钮 () 更新正在运行的应用程序。每次单击热重载或保存项目时,都会在正在运行的应用程序中随机选择不同的单词对。 这是因为单词对是在 build 方法内部生成的。每次 MaterialApp 需要渲染时或者在 Flutter Inspector 中切换平台时 build 都会运行.
添加一个 Stateful widget
Stateless widgets 是不可变的,这意味着它们的属性不能改变——所有的值都是 final。
Stateful widgets 持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少需要两个类:1)一个 StatefulWidget 类;2)一个 State 类,StatefulWidget 类本身是不变的,但是 State 类在 widget 生命周期中始终存在。
在这一步,你将添加一个 stateful widget(有状态的控件)—— RandomWords,它会创建自己的状态类 —— RandomWordsState,然后你需要将 RandomWords 内嵌到已有的无状态的 MyApp widget。
创建一个 state 类
这个类可以在任意地方创建而不一定非要在 MyApp 里,我们这里是放在 MyApp 类里边了:
1/**
2 * 创建一个 state 类,这个类可以在任意地方创建而不一定非要在 MyApp 里,
3 * 我们的示例代码是放在 MyApp 类的最下面了
4 * 注意一下 State<RandomWords> 的声明。
5 * 这表明我们在使用专门用于 RandomWords 的 State 泛型类。
6 * 应用的大部分逻辑和状态都在这里 —— 它会维护 RandomWords 控件的状态。
7 * 这个类会保存代码生成的单词对,这个单词对列表会随着用户滑动而无限增长,另外还会保存用户喜爱的单词对(第二部分),
8 * 也即当用户点击爱心图标的时候会从喜爱的列表中添加或者移除当前单词对。
9 * 重写 build 方法,该方法通过将生成单词对的代码从上边 MyApp 移动到 RandomWordsState 来生成单词对。
10 */
11class RandomWordsState extends State<RandomWords> {
12 @override
13 Widget build(BuildContext context) {
14 final WordPair wordPair=new WordPair.random();
15 return new Text(wordPair.asPascalCase););
注意一下 State
的声明。这表明我们在使用专门用于 RandomWords 的 State 泛型类。应用的大部分逻辑和状态都在这里 —— 它会维护 RandomWords 控件的状态。这个类会保存代码生成的单词对,这个单词对列表会随着用户滑动而无限增长,另外还会保存用户喜爱的单词对(第二部分),也即当用户点击爱心图标的时候会从喜爱的列表中添加或者移除当前单词对。RandomWordsState 继承自 RandomWords,我们接下来会创建这个类
添加有状态的 RandomWords widget 到 main.dart
RandomWords widget 除了创建 State 类之外几乎没有其他任何东西:
重写里边的build方法,该方法通过将生成单词对的代码从 MyApp 移动到 RandomWordsState 来生成单词对。
1/**
2 * 添加有状态的 RandomWords widget 到 main.dart,
3 * RandomWords widget 除了创建 State 类之外几乎没有其他任何东西:
4 */
5class RandomWords extends StatefulWidget {
6 @override
7 RandomWordsState createState() => new RandomWordsState();
8}
修改MyApp 里生成文字的代码:
1class MyApp extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 final WordPair wordPair = new WordPair.random(); // 删掉本行
5 return new MaterialApp(
6 title: 'Welcome to Flutter',
7 home: new Scaffold(
8 appBar: new AppBar(
9 title: new Text('Welcome to Flutter'),
10 ),
11 body: new Center(
12 //child: new Text(wordPair.asPascalCase), // 修改本行内容
13 child: new RandomWords(), // 修改成本行代码
14 ),
15 ),
16 );
17 }
18}
创建一个无限滚动的 ListView
扩展(继承)RandomWordsState 类,以生成并显示单词对列表。 当用户滚动时,ListView 中显示的列表将无限增长。 ListView 的 builder 工厂构造函数允许您按需建立一个懒加载的列表视图
向 RandomWordsState 类中添加一个 _suggestions 列表以保存建议的单词对,同时,添加一个 biggerFont 变量来增大字体大小
1class RandomWordsState extends State<RandomWords> {
2 // 添加如下两行
3 final List<WordPair> _suggestions = <WordPair>[];
4 final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
5 ...
6}
向 RandomWordsState 类添加一个 _buildSuggestions() 函数,此方法构建显示建议单词对的 ListView。
ListView 类提供了一个 builder 属性,itemBuilder 值是一个匿名回调函数, 接受两个参数- BuildContext 和行迭代器 i。迭代器从 0 开始, 每调用一次该函数,i 就会自增 1,对于每个建议的单词对都会执行一次。该模型允许建议的单词对列表在用户滚动时无限增长
1/**
2 * 向 RandomWordsState 类添加 _buildSuggestions() 函数
3 * itemBuilder 值是一个匿名回调函数, 接受两个参数- BuildContext 和行迭代器 i。
4 * 迭代器从 0 开始, 每调用一次该函数,i 就会自增 1,对于每个建议的单词对都会执行一次。
5 * 该模型允许建议的单词对列表在用户滚动时无限增长。
6 */
7 Widget _buildSuggestions() {
8 return new ListView.builder(
9 padding: const EdgeInsets.all(16.0),
10 // 对于每个建议的单词对都会调用一次 itemBuilder,
11 // 然后将单词对添加到 ListTile 行中
12 // 在偶数行,该函数会为单词对添加一个 ListTile row.
13 // 在奇数行,该函数会添加一个分割线 widget,来分隔相邻的词对。
14 // 注意,在小屏幕上,分割线看起来可能比较吃力。
15 itemBuilder: (BuildContext _content,int i){
16 if(i.isOdd){
17 // 在每一列之前,添加一个1像素高的分隔线widget
18 return const Divider();
19 }
20 // 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整)
21 // 比如 i 为:1, 2, 3, 4, 5 时,结果为 0, 1, 1, 2, 2,
22 // 这可以计算出 ListView 中减去分隔线后的实际单词对数量
23 final int index=i ~/2;
24 // 如果是建议列表中最后一个单词对
25 if(index>=_suggetions.length){
26 // ...接着再生成10个单词对,然后添加到建议列表
27 _suggetions.addAll(generateWordPairs().take(10));
28
29 }
30 return _buildRow(_suggetions[index]);
31 },
32 );
33 }
对于每一个单词对,_buildSuggestions 函数都会调用一次 _buildRow,所以我们创建_buildRow
1Widget _buildRow(WordPair pair){
2 return new ListTile(
3 title: new Text(
4 pair.asPascalCase,
5 style: _biggerFont,
6 ),
7 );
8
9 }
更新 RandomWordsState 的 build 方法以使用 _buildSuggestions(),而不是直接调用单词生成库
1class RandomWordsState extends State<RandomWords> {
2// 在 Dart 语言中使用下划线前缀标识符,会强制其变成私有。
3 final List<WordPair> _suggetions = <WordPair>[];
4 final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
5 @override
6 Widget build(BuildContext context) {
7 // 使用 Scaffold 类实现基础的 Material Design 布局
8 return new Scaffold(
9 appBar: new AppBar(
10 title: const Text('Startup Name Generator'),
11 ),
12 body: _buildSuggestions(),
13 );
14
15// final WordPair wordPair=new WordPair.random();
16// return new Text(wordPair.asPascalCase);
17
18 }
更新 MyApp 的 build 方法
1class MyApp extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 final wordPair = new WordPair.random(); //新增了这一行
5 return new MaterialApp(
6 title: 'Flutter 下的List列表',
7 home: new RandomWords(),
8// title: 'Welcome to Flutter',
9// home: new Scaffold(// 使用 Scaffold 类实现基础的 Material Design 布局
10// appBar: new AppBar(
11// title: const Text('Welcome to Flutter'),
12// ),
13//// body: const Center(//conset指代常量
14//// child: const Text('Hello World'),我们不用这样的方式生成文字了
15//// ),
16// body: new Center(
17// // 这里把之前的 "const" 换成了 "new".
18//// child: new Text(wordPair.asPascalCase), // 这是新的文字生成方式
19// child: new RandomWords(),
20// ),
21// ),
22 );
23 }
24}
效果图
代码结构目录
对于刚刚上手Flutter的同学,为了更方便的运行自己的demo,毕竟是第一个项目,把格式目录图贴给大家。
以上是关于Flutter控件篇(Stateful widget)——ListView的主要内容,如果未能解决你的问题,请参考以下文章
Flutter: Stateful 挂件 vs Stateless 挂件
Flutter 中 stateless 和 stateful widget 的区别[Flutter专题60]
从 Flutter 中的 Stateful Widget 返回数据