带你Flutter带你Fly之单词收藏
本次笔者将实现这样一个效果:收藏列表的单词。并可点击页面右上角按钮展示收藏的单词
收藏个别单词.png准备工作
打开Vs Code编辑器,快捷键 Crtl+Shift+P 打开 Commadn Palette命令板,输入 >Flutter:New Project 新建一个Flutter应用
输入应用名后,保存文件后,项目会自动进行创建,创建完毕之后,main.dart 文件会被自动打开。接下来,我们就对这个 main.dart 进行修改
一、熟悉 main.dart
把main.dart默认的代码替换成如下代码,建议手动敲打
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "First Flutter App",
home: new Scaffold(
appBar: new AppBar(
title: new Text("My First Flutter App "),
),
body: new Center(
child: new Text("This is my first flutter app"),
),
),
);
}
}
打开模拟器或者真机,按F5,运行代码
1.png代码还是比较简单的,主要有如下几个知识点:
- main 方法采用了胖箭头 ( => ) 表示法,这是一种用于单行函数或方法的简写。
- 该app继承了使它本身成为一个 widget 的 StatelessWidget 类。在 Flutter 中,大多数时候一切都可以看作 widget , 包括 alignment,padding 和 layout
- Material 库中的 Scaffold widget 提供了默认的应用栏 (app bar),标题和构成主页面 widget 树结构的 body 属性
- widget 的主要工作是提供一个build()方法,描述如何根据其他更低层级的 widget,来对这个 widget 进行展示
- widget 树由包含了 Text child widget 的 Center widget 组成。Center widget 可将它的所有子树对齐到屏幕中心
二、使用外部package
这一步我们学习引入外部的package来随机生成一对英文单词。借助名为 english_words的开源软件包,它包含数千个最常用的英文单词以及一些实用功能
1.在 pubspec.yaml 文件中, 将 english_words(3.1.0或更高版本)添加到依赖列表
添加依赖库.png2.Crtl+S 保存后它会自动将包拉入到项目中。或者直接控制台输入命令 flutter packages get
代码中导入该库
import 'package:english_words/english_words.dart';
更改一下代码,重新启动应用,这个时候会通过热重载的方式加载,速度很快,这个感觉是很爽的。
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordpair=new WordPair.random(); //随机生成一对单词
return new MaterialApp(
title: "first flutter app",
home: new Scaffold(
appBar: new AppBar(
title: new Text("My First Flutter App "),
),
body: new Center(
//child: new Text("This is my first flutter app"),
child: new Text(wordpair.asCamelCase), //驼峰方式命名随机生成的单词
),
),
);
}
}
first app.png
三、添加有状态的widget
在前面的代码中, MyApp 类继承的是 StatelessWidget 类。但Stateless widgets是不可改变的,这意味着它们的属性不能改变,所有的值都是 final 的
实现一个有状态的 widget 至少需要两个类:StatefulWidgets类和State类,其中StatefulWidgets类创建了一个State类的实例。StatefulWidget类本身是不可变的,但State类可存在于Widget的整个生命周期中
1.创建一个 RandomWords widget。除了创建 State 类之外几乎没有任何其他代码
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() => new RandomWordsState();
}
- 创建 RandomWordsState 类。这个类保存了 RandomWords widget 的状态,该应用程序的大部分代码都放在该类的 build 方法中。
class RandomWordsState extends State<RandomWords>{
@override
Widget build(BuildContext context) {
final wordPair=new WordPair.random();
return new Text(wordPair.asCamelCase);
}
3.修改 MyApp 中代码,重新启动应用,热重载后,效果和上图一样
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//final wordpair=new WordPair.random(); //随机生成一对单词
return new MaterialApp(
title: "first flutter app",
home: new Scaffold(
appBar: new AppBar(
title: new Text("My First Flutter App "),
),
body: new Center(
//child: new Text("This is my first flutter app"),
// child: new Text(wordpair.asCamelCase), //驼峰方式命名随机生成的单词
child: new RandomWords(), //直接调用自定义的 RandomWords widget
),
),
);
}
}
四、创建一个ListView展示英文单词
生成并展示词组列表,需要扩展 RandomWordsState 类。当用户滑动列表,ListView widget 中显示的列表将无限增长。 ListView 的 builder 工厂构造函数允许按需建立一个延迟加载的列表 view。
1.准备一个数组用来存储随机生成的单词
class RandomWordsState extends State<RandomWords>{
var _suggestWords = <WordPair>[]; //下划线表示私有
//字体大小
final _biggerFont = const TextStyle(fontSize: 18.0);
}
- 向 RandomWordsState 类添加一个_buildSuggestionWords() 方法,用于构建一个显示词组的 ListView。并调用 _buildRow 方法将每一个单词放入ListView的对应的item上
Widget _buildSuggestionWords(){
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i){
if(i.isOdd) return new Divider(); //listview的条目是奇数,画一条分割线
final index = i ~/2; // ~/ 表示除的结果取整
if(index >= _suggestWords.length){
_suggestWords.addAll(generateWordPairs().take(10)); //生成10个单词放入数组中
}
return _buildRow(_suggestWords[index]);
},
);
}
- _buildRow 方法每次会在一个 ListTile widget中展示一条新词组
Widget _buildRow(WordPair suggestWord) {
return new ListTile(
title: new Text(suggestWord.asPascalCase,
style: _biggerFont,
),
);
}
4.更新 RandomWordsState 类的 build 方法来使用 _buildSuggestions() 函数,而不是直接调用单词生成库
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Generate English Words"),
),
body: _buildSuggestionWords(),
);
}
5.更新 MyApp 类的 build 方法,使用 RandomWords widget 来生成一个不断滚动的listview
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//final wordpair=new WordPair.random(); //随机生成一对单词
return new MaterialApp(
title: "first flutter app",
home: new RandomWords(),
);
}
}
未收藏.png
五、点击爱心收藏
为每一行添加可点击的心形图标。当用户点击列表中的条目,切换其“收藏”状态,词组就会添加到收藏栏,或从已保存词组的收藏栏中删除。
1.添加一个 Set 集合 _saved 到 RandomWordsState 类。保存用户收藏的词组。Set 集合比 List 更适用于此,因为它不允许重复元素。
2.在 _buildRow 函数中,添加 alreadySaved 标志检查来确保一个词组还没有被添加到收藏,并添加一个心形图标来使用收藏功能
Widget _buildRow(WordPair suggestWord) {
final alreadySaved = _saved.contains(suggestWord);
return new ListTile(
title: new Text(suggestWord.asPascalCase,
style: _biggerFont,
),
trailing: new Icon(//根据单词是否收藏决定是否点亮爱心
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
)
);
}
3.虽然上面出现了心形图标,但还没有交互功能。在 _buildRow 函数中使心形可点击。如果词条已经被加入收藏,再次点击它将从收藏中删除
Widget _buildRow(WordPair suggestWord) {
final alreadySaved = _saved.contains(suggestWord);
return new ListTile(
title: new Text(suggestWord.asPascalCase,
style: _biggerFont,
),
trailing: new Icon( //根据单词是否收藏决定是否点亮爱心
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: (){ //点击每个条目进行收藏单词和取消收藏
setState(() {
if(alreadySaved)
_saved.remove(suggestWord);
else
_saved.add(suggestWord);
});
} ,
);
}
收藏个别单词.png在 Flutter 的响应式风格框架中,调用 setState() ,将为 State 对象触发 build() 方法的调用,从而实现对UI的更新。
六、跳转到收藏页面
添加一个显示收藏夹的新页面(在 Flutter 中称为 route(路由))。在 Flutter 中, Navigator 管理着包含了应用程序所有路由的一个堆栈。将一个路由push到 Navigator 的堆栈,将显示更新为新页面路由。将一个路由 pull 出 Navigator 的堆栈,显示将返回到前一个页面路由。
1.在 RandomWordsState 类的 build 方法中,向 AppBar 添加一个列表图标。当用户点击列表图标时,跳转到收藏的页面
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Generate English Words"),
actions: <Widget>[
//添加列表图标按钮,按下将调用_pushSaved 方法
new IconButton(icon: new Icon(Icons.format_list_bulleted),onPressed: _pushSaved),
],
),
body: _buildSuggestionWords(),
);
}
2.向 RandomWordsState 类添加一个 _pushSaved() 方法,这个方法用于跳转到收藏单词的页面
void _pushSaved(){
//跳转到新的页面
Navigator.of(context).push( //函数调用添加到 Navigator.push 中作为参数,如高亮代码所示,将路由 push 到 Navigator 的堆栈中。
new MaterialPageRoute(
builder: (context){
//把喜爱的单词放入listview的item中
final tiles=_saved.map(
(pair){
return new ListTile(
title: new Text(pair.asPascalCase,
style: _biggerFont,
),
);
},
);
//为每个 ListTile widget 之间添加水平间距。divided变量保存最终生成的所有行,并用 toList() 函数转换为列表。
final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
//新的页面,新页面的body属性由包含多个 ListTile widget 的 ListView 组成。
return new Scaffold(
appBar: new AppBar(
title: new Text('Favorite Words')),
body: new ListView(children: divided),
);
},
),
);
}
}
展示收藏单词.png