1. 实现一个可以收藏、无限滚动的随机单词列表,点击收藏按钮进入收藏页。
步骤
第一步:使用Android Studio创建项目。显示Hello World。
第二步:添加english_words依赖包,创建一个StatefulWidget。将Hello World改为随机字符串
第三步:创建一个ListView,点击收藏按钮跳转到收藏页
![](https://img.haomeiwen.com/i5111884/bbb9e381f4700f7b.gif)
第一步:使用Android Studio创建项目
![](https://img.haomeiwen.com/i5111884/518702cf681bb89b.png)
![](https://img.haomeiwen.com/i5111884/6ba8ff1306cbaaff.png)
![](https://img.haomeiwen.com/i5111884/48c957f7820ea2d7.png)
![](https://img.haomeiwen.com/i5111884/8c65504ce8a2c361.png)
![](https://img.haomeiwen.com/i5111884/2a0a5dadcf422784.png)
修改main.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.yellow,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: new Center(
child: new Text('Hello World'),
),
);
}
}
![](https://img.haomeiwen.com/i5111884/abbd24406665b8b4.png)
第二步:添加english_words依赖包,创建一个StatefulWidget(显示随机字符串)
1. pubspec.yaml文件将“english_words”(3.1.5版本)添加到依赖项列表,如下:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.0
# 新添加的依赖
english_words: ^3.1.5
2. 下载包。在Android Studio的编辑器视图中查看pubspec.yaml时,单击右上角的 Get dependencies 。这会将依赖包安装到项目。
命令行对应的命令:flutter packages get
3. main.dart文件中引入english_words包。
import 'package:english_words/english_words.dart';
4. 创建一个StatefulWidget,显示随机字符串
class RandomWordsWidget extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWordsWidget> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
修改MyHomePage
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: new Center(
child: new RandomWordsWidget(),
),
);
}
}
![](https://img.haomeiwen.com/i5111884/4f30e724523de50c.png)
![](https://img.haomeiwen.com/i5111884/bb2ee03b73b5e7d9.png)
![](https://img.haomeiwen.com/i5111884/5344825d9e04d945.png)
![](https://img.haomeiwen.com/i5111884/0c11429d95645d67.png)
第三步:创建一个ListView,点击收藏按钮跳转到收藏页
修改MyHomePage
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new RandomWordsWidget();
}
}
修改RandomWordsState
添加属性
// 存储单词
final _suggestions = <WordPair>[];
// 存储收藏的单词。Set比List更合适,因为Set中不允许重复的值。
final _saved = new Set<WordPair>();
// 字体大小
final _biggerFont = const TextStyle(fontSize: 18.0);
添加方法(显示ListView)
// 自定义方法:返回显示单词的ListView
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
/*
对于每个单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中
在偶数行,该函数会为单词对添加一个ListTile row.
在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。
注意,在小屏幕上,分割线看起来可能比较吃力。
i从0开始,每调用一次会自增1
*/
itemBuilder: (context, i) {
// 在每一列之前,添加一个1像素高的分隔线widget
if (i.isOdd) return new Divider();
/*
语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5时,结果为0, 1, 1, 2, 2。
这可以计算出ListView中减去分隔线后的实际单词对数量
*/
final index = i ~/ 2;
// 如果是列表中最后一个单词,接着再生成10个单词,然后添加到收藏列表,无限循环。
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
// 自定义方法(每一行的UI)
Widget _buildRow(WordPair pair) {
// 检查确保单词对有没有添加到收藏夹中
final alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
// 尾部设置一个收藏图标
trailing: new 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(
new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('CollectionPage'),
),
body: new ListView(children: divided),
);
},
),
);
}
修改build方法
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Home'),
// 收藏按钮
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(),
);
}
![](https://img.haomeiwen.com/i5111884/9776a6c66cde8c41.png)
![](https://img.haomeiwen.com/i5111884/c5dfc7aeba557d6f.png)
2. 布局练习
![](https://img.haomeiwen.com/i5111884/bd4a20d087dec017.png)
![](https://img.haomeiwen.com/i5111884/1115b484567f4473.png)
分析布局
最外层使用ListView(默认:纵向滚动),允许滚动(保证在小屏幕手机上也可以查看超出屏幕的内容)。
分为4部分:
1. 图片
2. 标题、副标题、图标和收藏个数
3. 3个按钮(上面图标,下面文字)
4. 介绍
步骤
1. 项目根目录下创建imgs目录,存放timg.jpg
2. pubspec.yaml文件下配置timg.jpg图片
flutter:
assets:
- imgs/timg.jpg
3. main.dart文件内容如下,主要工作在children中
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
//。。。
return new Scaffold(
// ListView:可滚动
body: new ListView(
children: [
// 。。。
],
),
);
}}
4. 图片
在build下创建,然后添加imgSection到children中
Widget imgSection = new Image.asset(
'imgs/timg.jpg',
height: 240.0,
fit: BoxFit.cover,
);
5. 标题、副标题、图标和收藏个数
在build下创建,然后添加titleSection到children中
Widget titleSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Row(
children: [
// Expanded:占满额外的空间(占满除了图标喝收藏个数之外的空间)
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: new Text(
'东北虎',
style: new TextStyle(
fontWeight: FontWeight.bold,
),
),
),
new Text(
'又称西伯利亚虎',
style: new TextStyle(
color: Colors.grey[500],
),
),
],
),
),
new Icon(
Icons.star,
color: Colors.yellow[500],
),
new Text('110'),
],
),
);
6. 3个按钮(上面图标,下面文字)
在build下创建,然后添加buttonSection到children中
Column buildButtonColumn(IconData icon, String label) {
Color color = Theme.of(context).primaryColor;
return new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Icon(icon, color: color),
new Container(
margin: const EdgeInsets.only(top: 8.0),
child: new Text(
label,
style: new TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}
Widget buttonSection = new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildButtonColumn(Icons.description, '详情'),
buildButtonColumn(Icons.near_me, '路线'),
buildButtonColumn(Icons.share, '分享'),
],
),
);
7. 介绍
在build下创建,然后添加buttonSection到children中
Widget textSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Text(
'''
虎(学名:Panthera tigris ;英文名:Tiger):是哺乳纲的大型猫科动物;毛色浅黄或棕黄色,满身黑色横纹;头圆、耳短,耳背面黑色,中央有一白斑甚显著;四肢健壮有力;尾粗长,具黑色环纹,尾端黑色。老虎是典型的山地林栖动物,由南方的热带雨林、常绿阔叶林,以至北方的落叶阔叶林和针阔叶混交林,都能很好的生活。在中国东北地区,也常出没于山脊、矮林灌丛和岩石较多或砾石塘等山地,以利于捕食。虎常单独活动,只有在繁殖季节雌雄才在一起生活。无固定巢穴,多在山林间游荡寻食。能游泳。由于林区开发、人口激增,过去偏远地区都已发展为村镇,虎亦常到林区居民点附近觅食。虎多黄昏活动,白天多潜伏休息,没有惊动则很少出来。虎的活动范围较大,在北方日寻食活动范围可达数十公里;在南方西双版纳因食物较多则活动距离较短。
''',
softWrap: true,
),
);
8. 使收藏按钮可点击
创建一个有状态组件(包含收藏图标和个数),使用FavoriteWidget()替换上面代码中的相应部分。
class FavoriteWidget extends StatefulWidget {
@override
_FavoriteWidgetState createState() => new _FavoriteWidgetState();
}
// _开头代表私有
class _FavoriteWidgetState extends State<FavoriteWidget> {
// 是否收藏
bool _isFavorited = true;
// 收藏个数
int _favoriteCount = 110;
// 点击收藏按钮后调用
void _toggleFavorite() {
// 改变状态值,更新UI
setState(() {
if (_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});
}
@override
Widget build(BuildContext context) {
return new Row(
mainAxisSize: MainAxisSize.min,
children: [
new Container(
padding: new EdgeInsets.all(0.0),
child: new IconButton(
icon: (_isFavorited
? new Icon(Icons.star)
: new Icon(Icons.star_border)),
color: Colors.yellow[500],
onPressed: _toggleFavorite,
),
),
// 将文本放在SizedBox中并设置其宽度可防止出现明显的跳跃
new SizedBox(
width: 25.0,
child: new Container(
child: new Text('$_favoriteCount'),
),
),
],
);
}
}
网友评论