Flutter 系列---入门篇
Flutter 系列---创建APP
第1步: 创建 Flutter app
创建一个简单的、基于模板的Flutter应用程序。
我们上一篇配置好了开发环境后,打开 Andriod Studio
就可以看到多了一项 Start a new Flutter project
(或者总得命令创建:flutter create projectName)
1. 替换 lib/main.dart.
创建好后,看到左侧目录,找到 main.dart ,
删除lib / main.dart中的所有代码,然后替换为下面的代码,它将在屏幕的中心显示“Hello World”.
// 入口:main 函数开始
void main() => runApp(HelloWorld());
//====================================================
// Hello World
//====================================================
class HelloWorld extends StatelessWidget {
//每个部件渲染时都是会运行 build 函数.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Hello World'),
),
body: new Center(
child: new Text('Hello World',style: TextStyle(fontSize: 50)),
),
),
);
}
}
分析:
- 本示例创建一个
Material APP
。Material
是一种标准的移动端和web端的视觉设计语言。Flutter
提供了一套丰富的Material widgets
。 main
函数使用了(=>)
符号, 这是Dart
中单行函数或方法的简写。- 该应用程序继承了
StatelessWidget
,这将会使应用本身也成为一个widget
。 在Flutter
中,大多数东西都是widget
,包括对齐(alignment)
、填充(padding)
和布局(layout)
Scaffold
是Material library
中提供的一个widget
, 它提供了默认的导航栏、标题和包含主屏幕widget
树的body
属性。widget
树可以很复杂。widget
的主要工作是提供一个build()
方法来描述如何根据其他较低级别的widget
来显示自己。- 本示例中的
body
的widget
树中包含了一个Center widget
,Center widget
又包含一个Text
子widget
。Center widget
可以将其子widget
树对其到屏幕中心。 - Flutter 里有非常多种类的部件 widget,最终继承的父类都是 widget。需要什么就造什么控件!
2. 运行应用程序,你应该看到如下界面
第2步: 使用外部包(package)
在这一步中,将开始使用一个名为english_words
的开源软件包 ,其中包含数千个最常用的英文单词以及一些实用功能.
1. pubspec 文件管理Flutter
应用程序的assets
(资源,如图片、package
等)
在pubspec.yaml
中,将english_words
(3.1.0或更高版本)添加到依赖项列表,如下:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
english_words: ^3.1.0
2. 在 lib/main.dart 中, 引入 english_words
如下所示:
import 'package:english_words/english_words.dart';
3. 使用 English words 包生成文 本来替换字符串“Hello World”
//====================================================
// Hello World
//====================================================
class HelloWorld extends StatelessWidget {
//每个部件渲染时都是会运行 build 函数.
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Hello World'),
),
body: new Center(
// child: new Text('Hello World',style: TextStyle(fontSize: 50)),
// 用刚才导入的第三方库,生产随机单词,并返回驼峰命名
child: new Text(wordPair.asCamelCase,style: TextStyle(fontSize: 50)),
),
),
);
}
}
4. 运行
如果应用程序正在运行,请使用热重载按钮 (lightning bolt icon) 更新正在运行的应用程序。
每次单击热重载或保存项目时,都会在正在运行的应用程序中随机选择不同的单词对。
这是因为单词对是在 build 方法内部生成的。
每次MaterialApp需要渲染时或者在Flutter Inspector中切换平台时 build 都会运行.
第3步: 添加一个 有状态的部件(Stateful widget)
Stateless widgets 是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终的.
Stateful widgets 持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类:
一个 StatefulWidget类。
一个 State类。 StatefulWidget类本身是不变的,但是 State类在widget生命周期中始终存在.
在这一步中,我们将添加一个有状态的widget-RandomWords
,它创建其State
类RandomWordsState
。State
类将最终为widget
维护一个单词列表。
1. 添加有状态的 RandomWords widget 到 main.dart
它也可以在MyApp之外的文件的任何位置使用。RandomWords widget除了创建State类之外几乎没有其他任何东西
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState(); }
2. 添加 RandomWordsState 类
该应用程序的大部分代码都在该类中,该类持有RandomWords widget的状态。
这个类将保存随着用户滚动而无限增长的生成的单词对, 以及喜欢的单词对,用户通过重复点击心形 ❤️ 图标来将它们从列表中添加或删除。
首先,通过添加高亮显示的代码创建一个最小类:
class RandomWordsState extends State{
}
3. 在添加状态类后,IDE会提示该类缺少build方法
接下来,您将添加一个基本的build方法,该方法通过将生成单词对的代码从 MyApp 移动到 RandomWordsState 来生成单词对。
将 build 方法添加到 RandomWordState 中,如下面代码所示:
class RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
4. 通过下面的代码,将生成单词对代的码从MyApp移动到RandomWordsState中
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//final wordPair = new WordPair.random(); // 删除此行
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar( title: new Text('Welcome to Flutter'),
),
body: new Center(
//child: new Text(wordPair.asPascalCase),//替换这行
child: new RandomWords(),
),
),
);
}
}
重启应用
第4步: 创建一个无限滚动ListView
1. 在这一步中,将扩展(继承)RandomWordsState类,以生成并显示单词对列表
当用户滚动时,ListView 中显示的列表将无限增长。 ListView 的 builder 工厂构造函数允许您按需建立一个懒加载的列表视图。
向 RandomWordsState 类中添加一个 _suggestions 列表以保存单词对。 该变量以下划线(_)开头,在 Dart 语言中使用下划线前缀标识符,会强制其变成私有的。
另外,添加一个 biggerFont 变量来增大字体大小
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
2. 向 RandomWordsState 类添加一个 _buildSuggestions() 函数
此方法构建显示建议单词对的ListView。
ListView 类提供了一个 builder属性,itemBuilder 值是一个匿名回调函数, 接受两个参数- BuildContext 和行迭代器i。迭代器从 0 开始, 每调用一次该函数,i 就会自增1,对于每个建议的单词对都会执行一次。该模型允许建议的单词对列表在用户滚动时无限增长
class RandomWordsState extends State<RandomWords> {
...
Widget _buildSuggestions() {
// 返回一个 list view 列表视图
return new ListView.builder(
padding: const EdgeInsets.all(16),
// 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中
// 在偶数行,该函数会为单词对添加一个ListTile row.
// 在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。
// 注意,在小屏幕上,分割线看起来可能比较吃力。
itemBuilder: (context, i){
// 在每一列之前,添加一个1像素高的分隔线widget
if (i.isOdd) return new Divider();//i 是基数,就添加一条横线
// 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5
// 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量
final index = i ~/2;
// 如果是建议列表中最后一个单词对
if (index >= _suggestions.length) {
// ...接着再生成10个单词对,然后添加到建议列表
_suggestions.addAll(generateWordPairs().take(10));// 每次增加 10 个单词
}
return _buildRow(_suggestions[index]);
},
);
}
3. 构建每一行视图
对于每一个单词对,_buildSuggestions 函数都会调用一次 _buildRow。 这个函数在 ListTile 中显示每个新词对,这使您在下一步中可以生成更漂亮的显示行
在 RandomWordsState 中添加一个 _buildRow 函数 :
class RandomWordsState extends State{
...
Widget _buildRow(WordPair pair) {
return new ListTile(title: new Text( pair.asPascalCase, style: _biggerFont ),
);
}
}
4. 更新 build 方法
更新RandomWordsState的build方法以使用_buildSuggestions(),而不是直接调用单词生成库。 更改后如下面部分:
@override
Widget build(BuildContext context) {
return new Scaffold (
appBar: new AppBar( title: new Text('Startup Name Generator'),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.list),
onPressed: _pushSave
),
],
),
body: _buildSuggestions(),
);
}
重新启动应用程序。你应该看到一个单词对列表。尽可能地向下滚动,您将继续看到新的单词对。
第5步: 添加交互
1. 添加图片
在这一步中,您将为每一行添加一个可点击的心形 ❤️ 图标。当用户点击列表中的条目,切换其“收藏”状态时,将该词对添加到或移除出“收藏夹”。
添加一个 _saved Set(集合) 到RandomWordsState。这个集合存储用户喜欢(收藏)的单词对。 在这里,Set比List更合适,因为Set中不允许重复的值。
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
//添加一个 _saved Set(集合)
final _saved = new Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
2. 添加点击事件
在 _buildRow 方法中添加图标和点击事件
- 添加 alreadySaved 来检查确保单词对还没有添加到收藏夹中;
- 添加一个心形 ❤️ 图标到 ListTiles以启用收藏功能;
- 让心形❤️图标变得可以点击,当心形❤️图标被点击时,函数调用 setState() 通知框架状态已经改变。
代码如下:
Widget _buildRow(WordPair pair) {
final alreadSaved = _saved.contains(pair);
return new ListTile(
// cell 的标题
title: new Text(
pair.asPascalCase,//驼峰命名
style: _biggerFont,
),
// 添加 ❤ 图片
trailing: new Icon(
alreadSaved ? Icons.favorite : Icons.favorite_border,
color: alreadSaved ? Colors.red : null,
),
// 添加点击事件
onTap: () {
setState(() {
if (alreadSaved) {
_saved.remove(pair);
}else{
_saved.add(pair);
}
});
},
);
}
提示: 在Flutter的响应式风格的框架中,调用setState() 会为State对象触发build()方法,从而导致对UI的更新
第6步: 导航到新页面
在这一步中,将添加一个显示收藏夹内容的新页面(在Flutter中称为路由(route))。在主路由和新路由之间导航(切换页面)。
在 Flutter中,导航器管理应用程序的路由栈。将路由推入(push)到导航器的栈中,将会显示更新为该路由页面。 从导航器的栈中弹出(pop)路由,将显示返回到前一个路由。
1. 新新建一个新页面
这个页面也是一个列表,列表显示的数据由上一个页面传值过来,新页面代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:english_words/english_words.dart';
class SavedPage extends StatelessWidget {
final Set divided ;
SavedPage({Key key, this.divided}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
backgroundColor: Colors.brown,
title: new Text(
"Save Words Page",
style: TextStyle(fontSize: 20),
),
leading: IconButton(
icon: new Image.asset("assets/images/nav_close.png"),
onPressed: (){
Navigator.maybePop(context);
}),
),
body: new ListView.builder(
padding: EdgeInsets.all(10),
itemCount: divided.length*2,//数组长度 + 分割线
itemBuilder: (context,i){
if (i.isOdd)
return new Divider();//i 是基数,就添加一条横线
final index = i ~/2;
final WordPair a = divided.elementAt(index);
return new ListTile(title: new Text(a.asCamelCase));
},
),
);
}
}
3. 添加 _pushSaved 方法
- 当用户点击导航栏中的列表图标时,建立一个路由并将其推入到导航管理器栈中。此操作会切换页面以显示新路由。
- 添加 Navigator.push调用,这会使路由入栈(以后路由入栈均指推入到导航管理器的栈)
- 在新页面的构造方法传递数据
向RandomWordsState类添加一个 _pushSaved() 方法.
class RandomWordsState extends State<RandomWords> {
···
void _pushSave() {
print("push save page");
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context) => new SavedPage(divided: _saved)
),
);
}
···
}
4. 热重载应用程序
你已经编写了一个可以在iOS和Android上运行的交互式Flutter应用程序。
四、总结
在这整个过程中,我们已经做了下面这些事:
- 环境配置
- 从头开始创建一个 Flutter 应用程序.
- 编写 Dart 代码.
- 利用外部的第三方库.
- 使用热重载加快开发周期.
- 实现一个有状态的 widget ,为你的应用增加交互.
- 用 ListView 和 ListTiles 创建一个延迟加载的无限滚动列表.
- 不同页面之间跳转 和 传值
0条评论