学习完官网案例后换汤不换药完成了一个词汇本的实例,特此记录。
1. 基础版
先做一个能够无限翻页的实例。
main.dart
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
// final wordPair = WordPair.random();
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
),
// body: const Center(
// child: Text('Hello World'),
// ),
// body: Center(
// child: Text(wordPair.asPascalCase),
body: const Center(
child: RandomWords(),
),
),
);
}
}
// 输入stful会自动跳提示的,然后就可以在样板上操作了
//
class RandomWords extends StatefulWidget {
// const RandomWords({Key? key}) : super(key: key);
const RandomWords({super.key});
// 状态类继承自 State<RandomWords>
// State<RandomWords> createState() => _RandomWordsState();
State<RandomWords> createState() => _RandomWordsState();
}
// _是为了增强隐私性,是针对 State 对象推荐的最佳实践写法
// 重写下build的代码,修改为胜场随机字符的Text
class _RandomWordsState extends State<RandomWords> {
//
final _suggestions = <WordPair>[];
// 设置字体
final _biggerFont = const TextStyle(fontSize: 16);
Widget build(BuildContext context) {
// return Container();
// final wordPair = WordPair.random();
// return Text(wordPair.asPascalCase);
return ListView.builder(
padding: const EdgeInsets.all(26.0),
// 对每一个单词都用itemBuilder
itemBuilder: (context, i) {
// 在每行前加分割线,即i为奇数的时候
if (i.isOdd) return const Divider();
// /2向下取整,表实际单词数
final index = i ~/ 2;
// 单词数不够的时候,加10个单词
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
// 不是奇数的时候放单词
return ListTile(
title: Text(
// 每次return当前单词
_suggestions[index].asPascalCase,
// 设置style
style: _biggerFont,
),
);
},
);
}
}
2. 进阶版
在flutter中,界面切换称作路由切换,进阶版中我们将添加一个主路由切换新路由的操作。flutter的导航器是一个栈,新界面会被push到栈中,当推出这个界面时,会被pop出来。
代码实现如下,注释写的很详细了。
main.dart
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
// 这个歌东西特别有用,是用来决定整个项目的主题的。
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.blue,
),
home: const Scaffold(
// 效果和上面的title重复了
// appBar: AppBar(
// title: const Text('Welcome to Flutter'),
// ),
body: Center(
child: RandomWords(),
),
),
);
}
}
// 输入stful会自动跳提示的,然后就可以在样板上操作了
//
class RandomWords extends StatefulWidget {
// const RandomWords({Key? key}) : super(key: key);
const RandomWords({super.key});
// 状态类继承自 State<RandomWords>
// State<RandomWords> createState() => _RandomWordsState();
State<RandomWords> createState() => _RandomWordsState();
}
// _是为了增强隐私性,是针对 State 对象推荐的最佳实践写法
// 重写下build的代码,修改为胜场随机字符的Text
class _RandomWordsState extends State<RandomWords> {
// 存储单词
final List<WordPair> _suggestions = <WordPair>[];
// 设置单词是否被标注为收藏,这里必须声明_liked是一个<WordPair>
final Set<WordPair> _liked = Set<WordPair>();
// 设置字体
final _biggerFont = const TextStyle(fontSize: 18);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Clark\'s Homework'),
// 这里出现新的界面,我们加入图标
actions: <Widget>[
IconButton(icon: const Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildWords(),
);
}
// 这里显示
Widget _buildWords() {
// return Container();
// final wordPair = WordPair.random();
// return Text(wordPair.asPascalCase);
return ListView.builder(
padding: const EdgeInsets.all(26.0),
// 对每一个单词都用itemBuilder
itemBuilder: (context, i) {
// 在每行前加分割线,即i为奇数的时候
if (i.isOdd) return const Divider();
// /2向下取整,表实际单词数
final index = i ~/ 2;
// 单词数不够的时候,加10个单词
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
// 不是奇数的时候放单词和收藏
return _buildRow(_suggestions[index]);
});
}
// 为了给文字后面加爱心
Widget _buildRow(WordPair pair) {
// 表示这个单词是否被like了
final bool alreadySaved = _liked.contains(pair);
return ListTile(
// 放文字
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
// 放图标
trailing: Icon(
alreadySaved ? Icons.grade : Icons.verified,
color: alreadySaved ? Colors.yellow : null,
),
// 交互:点击前后图标的变换
onTap: () {
setState(() {
if (alreadySaved) {
_liked.remove(pair);
} else {
_liked.add(pair);
}
});
},
);
}
// 这里定义被点击后新的界面出现的流程
void _pushSaved() {
// 入栈
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _liked.map(
// (WordPair pair)这部分这样写报错(pair)不报错是因为前面没有定义liked是<WordPair>
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
// 负责组成一个容器,将context和tiles以list的形式合到一起,这俩都在builder里生成了
final List<Widget> divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
// 弹回一个新的界面的Scaffold,这个界面标题是Saved Suggestions,内容是一个devided
return Scaffold(
appBar: AppBar(
title: const Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
}