需求
- 完成一个简单的移动应用程序,功能是:为一个创业公司生成建议的名称。用户可以选择和取消选择的名称、保存(收藏)喜欢的名称。
- 该代码一次生成十个名称,当用户滚动时,会生成一新批名称。
- 用户可以点击导航栏右边的列表图标,以打开到仅列出收藏名称的新页面(route)。
这一部分,我们来写一下交互和打开新的页面
向列表里添加图标
将lib/main.dart的代码替换
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MaterialApp(
title: 'Startup Name Generator',
home: Scaffold(
appBar: AppBar(
title: const Text('Startup Name Generator'),
),
body: Center(
child: RandomWords(),
),
),
);
}
}
class RandomWords extends StatefulWidget {
const RandomWords({super.key});
@override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
//保存建议的单词对
final List<WordPair> _suggesttions = <WordPair>[];
// 这个集合存储用户喜欢的单词对。Set中不允许重复的值
final Set<WordPair> _saved = new Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18);
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i) {
if(i.isOdd) return const Divider();
final index = i~/2;
if(index >= _suggesttions.length){
_suggesttions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggesttions[index]);
},
);
}
Widget _buildRow(WordPair pair){
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved?Icons.favorite:Icons.favorite_border,
color: alreadySaved?Colors.red:null,
),
);
}
}
我们添加了一个函数用来展示列表的每行
可以看到效果如下
添加交互
下面我们给点击添加效果,这部分就是调用setState()通知框架状态已经改变
在Flutter的响应式风格的框架中,调用setState()会为State对象出发build()方法,从而导致对UI的更新。
将lib/main.dart代码更新为
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MaterialApp(
title: 'Startup Name Generator',
home: Scaffold(
appBar: AppBar(
title: const Text('Startup Name Generator'),
),
body: Center(
child: RandomWords(),
),
),
);
}
}
class RandomWords extends StatefulWidget {
const RandomWords({super.key});
@override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
//保存建议的单词对
final List<WordPair> _suggesttions = <WordPair>[];
// 这个集合存储用户喜欢的单词对。Set中不允许重复的值
final Set<WordPair> _saved = new Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18);
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i) {
if(i.isOdd) return const Divider();
final index = i~/2;
if(index >= _suggesttions.length){
_suggesttions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggesttions[index]);
},
);
}
Widget _buildRow(WordPair pair){
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved?Icons.favorite:Icons.favorite_border,
color: alreadySaved?Colors.red:null,
),
onTap:(){
setState(() {
if(alreadySaved){
_saved.remove(pair);
}else{
_saved.add(pair);
}
});
}
);
}
}
导航到新页面
替换掉lib/main.dart中的内容
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main(List<String> args) {
runApp(const MyApp());
}
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return const MaterialApp(
title: 'Startup Name Generator',
home: RandomWords()
);
}
}
class RandomWords extends StatefulWidget {
const RandomWords({super.key});
@override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
//保存建议的单词对
final List<WordPair> _suggesttions = <WordPair>[];
// 这个集合存储用户喜欢的单词对。Set中不允许重复的值
final Set<WordPair> _saved = Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Startup Name Generator'),
actions: <Widget>[
IconButton(onPressed: _pushSaved, icon: const Icon(Icons.list))
],
),
body: _buildSuggestions(),
);
}
Widget _buildSuggestions(){
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i) {
if(i.isOdd) return const Divider();
final index = i~/2;
if(index >= _suggesttions.length){
_suggesttions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggesttions[index]);
},
);
}
Widget _buildRow(WordPair pair){
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved?Icons.favorite:Icons.favorite_border,
color: alreadySaved?Colors.red:null,
),
onTap:(){
setState(() {
if(alreadySaved){
_saved.remove(pair);
}else{
_saved.add(pair);
}
});
}
);
}
void _pushSaved(){
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context){
final Iterable<ListTile> tiles = _saved.map((WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
});
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: const Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
}
我们在appBar那行增加一个IconButton图表,当用户点击列表图表的时候,包含收藏夹的新路由页面入栈显示
给onPressed绑定一个_pushSaved函数
因为我们需要在_RandomWordsState类中使用函数,所以将return 的ListView抽象成一个函数
在_pushSaved中
添加 Navigator.push 调用,这会使路由入栈(以后路由入栈均指推入到导航管理器的栈)
添加MaterialPageRoute ,新页面的内容会在MaterialPageRoute的build属性中构建
使用Themes修改UI
通过ThemeData类更改应用程序的主题
将类MyApp中的代码替换
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
colorScheme: const ColorScheme.light(
primary: Colors.orange,
onPrimary: Colors.white,
onBackground: Colors.white,
secondary: Colors.amber),
),
home: RandomWords()
);
}
}
这里注意单独使用theme:ThemeData(primaryColor: Colors.red),是无效的
需要设置主题中的 colorScheme属性
这里使用ColorScheme.light
theme: ThemeData(
colorScheme: const ColorScheme.light(
primary: Colors.orange,
onPrimary: Colors.white,
onBackground: Colors.white,
secondary: Colors.amber),
),
至此完成了一个可运行在 Android 和 iOS 系统上的、包含交互的 Flutter 应用,在这个 项目 里,你已经做了下面的事情:
- 写了 Dart 代码
- 使用热重载加速了开发进程
- 实现了一个 stateful widget,为你的应用加入了交互功能
- 创建了一个新的页面(route),为主页和这个新页面的跳转加入了逻辑
- 学会了如何使用 themes 修改应用的 UI