目录
- 从零开始写第一个Flutter app(一)——Hello World
- 从零开始写第一个Flutter app(二)——引用第三方包
- 从零开始写第一个Flutter app(三)——有状态的部件StatefulWidget
- 从零开始写第一个Flutter app(四)——无限滑动的ListView
- 从零开始写第一个Flutter app(五)——ListView添加图标
- 从零开始写第一个Flutter app(六)——添加点击事件交互
- 从零开始写第一个Flutter app(七)——弹出toast
- 从零开始写第一个Flutter app(八)——修改主题颜色
- 从零开始写第一个Flutter app(九)——route跳转新页面
- 从零开始写第一个Flutter app(十)——最终代码
前言
在贴出最终代码前,我们再对上一节的代码做下重构,使代码更清晰点。
在上节跳转新页面时,我们是在MaterialPageRoute的builder中写一个方法直接返回一个ListView,这一节我们重构下代码,新建一个StateLessWidget表示新的reoute,在里面返回ListView。这样结构会比较清晰一点。
重构前代码
先看下重构前跳转新页面的代码
void _pushSaved() {
// push方法往堆栈里添加一个新的Route对象
Navigator.of(context).push(
// MaterialPageRoute是Matetial风格的Route
MaterialPageRoute<void>(
// builder属性是一个方法,返回一个Widget
builder: (BuildContext context) {
// map操作符,将List<WordPair>转成Iterable<ListTile>
final Iterable<ListTile> tiles = _saved.map((WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
// 通过ListTile.divideTiles方法将每一个ListTile下面添加一条分割线
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
)
.toList();
// 通过Scaffold构造一个新的页面,这个就是新的Route页面的内容
return Scaffold( // Add 6 lines from here...
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
); // ... to here.
},
),
);
}
重构1
新建SavedRoute 表示新的页面route,由于该页面单纯显示,不需要管理状态,所以继承StateLessWidget
将原先在_pushSaved()方法中构建ListView的代码转移到SavedRoute类的build()方法下
class SavedRoute extends StatelessWidget {
// 构造函数,选中的数据saved由上个页面传过来
SavedRoute({Key key, this.saved}): super(key: key);
final List<WordPair> saved;
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
// map操作符,将List<WordPair>转成Iterable<ListTile>
final Iterable<ListTile> tiles = saved.map((WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
});
// 通过ListTile.divideTiles方法将每一个ListTile下面添加一条分割线
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
}
}
修改_pushSaved()方法
void _pushSaved() {
// push方法往堆栈里添加一个新的Route对象
Navigator.of(context).push(
// MaterialPageRoute是Matetial风格的Route
MaterialPageRoute<void>(
// builder属性是一个方法,返回一个Widget
builder: (context) => SavedRoute(saved: _saved.toList())
),
);
}
重构2
这里再提供另一种重构的方案,在方案一中ListView的构建是一次性生成所有item,这种方法适用于item数量少的情况,多的话会影响性能,如果item数量多建议使用ListView.builder来构建
class SavedRoute2 extends StatelessWidget {
// 构造函数,选中的数据saved由上个页面传过来
SavedRoute2({Key key, this.saved}): super(key: key);
final List<WordPair> saved;
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
// item数量
itemCount: saved.length,
itemBuilder: (context, i) {
// 先创建一个ListTile,此时还未有分割线
ListTile listTile = ListTile(
title: Text(
saved[i].asPascalCase,
style: _biggerFont,
),
);
// 添加分割线
DecoratedBox box = DecoratedBox(
position: DecorationPosition.foreground,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: listTile,
);
return box;
}),
);
}
}
void _pushSaved() {
// push方法往堆栈里添加一个新的Route对象
Navigator.of(context).push(
// MaterialPageRoute是Matetial风格的Route
MaterialPageRoute<void>(
// builder属性是一个方法,返回一个Widget
builder: (context) => SavedRoute2(saved: _saved.toList())
),
);
}
最终代码
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
import 'package:fluttertoast/fluttertoast.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData( // Add the 3 lines from here...
primaryColor: Colors.white,
),
home: RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
@override
RandomWordsState createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[]; // ListView用的数据源
final _biggerFont = const TextStyle(fontSize: 18.0); // 字体大小
final Set<WordPair> _saved = Set<WordPair>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
actions: <Widget>[ // Add 3 lines from here...
IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(), // body为一个ListView
);
}
Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0), // 设置padding
itemBuilder: (context, i) {
if (i.isOdd) return Divider(); // 如果为基数,返回分割线
final index = i ~/ 2; // 由于divider也占一个位置,所以需除以2计算实际的index
// 若数据源不够,则一次性生成10条数据,这样就可以实现ListView无限滑动的效果
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
// 生成每一条item布局
return _buildRow(_suggestions[index]);
});
}
// 创建ListView的Item
Widget _buildRow(WordPair pair) {
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont, // 设置样式字体大小
),
trailing: Icon( // Add the lines from here...
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () { // Add 9 lines from here...
setState(() {
if (alreadySaved) {
_saved.remove(pair);
toast("选择:" + pair.asPascalCase);
} else {
_saved.add(pair);
toast("取消:" + pair.asPascalCase);
}
});
},
);
}
void toast(String txt) {
Fluttertoast.showToast(
msg: txt, // 显示的内容
toastLength: Toast.LENGTH_SHORT, // 显示时长,与原生Toast一样可设置LENGTH_SHORT或者LENGTH_LONG
gravity: ToastGravity.BOTTOM, // 显示位置,支持TOP, BOTTOM, CENTER三种位置
timeInSecForIos: 1,
backgroundColor: Colors.black, // 背景颜色
fontSize: 16, // 字体大小
textColor: Colors.white // 字体颜色
);
}
void _pushSaved() {
// push方法往堆栈里添加一个新的Route对象
Navigator.of(context).push(
// MaterialPageRoute是Matetial风格的Route
MaterialPageRoute<void>(
// builder属性是一个方法,返回一个Widget
builder: (context) => SavedRoute(saved: _saved.toList())
// builder: (context) => SavedRoute2(saved: _saved.toList())
),
);
}
}
class SavedRoute extends StatelessWidget {
// 构造函数,选中的数据saved由上个页面传过来
SavedRoute({Key key, this.saved}): super(key: key);
final List<WordPair> saved;
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
// map操作符,将List<WordPair>转成Iterable<ListTile>
final Iterable<ListTile> tiles = saved.map((WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
});
// 通过ListTile.divideTiles方法将每一个ListTile下面添加一条分割线
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
}
}
class SavedRoute2 extends StatelessWidget {
// 构造函数,选中的数据saved由上个页面传过来
SavedRoute2({Key key, this.saved}): super(key: key);
final List<WordPair> saved;
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
// item数量
itemCount: saved.length,
itemBuilder: (context, i) {
// 先创建一个ListTile,此时还未有分割线
ListTile listTile = ListTile(
title: Text(
saved[i].asPascalCase,
style: _biggerFont,
),
);
// 添加分割线
DecoratedBox box = DecoratedBox(
position: DecorationPosition.foreground,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: listTile,
);
return box;
}),
);
}
}