是时候搞一点新东西了

其实我在去年就对 Flutter 有所了解,当时的 Flutter 已经可以和 react native 相互竞争了,但是但是 flutter 生态化没有完全建立起来,根本和 RN 比不了,虽然在 Github 有了几十万 star 但是也只是作为 Android 和 iOS 之间跨平台的一种方案。

然而在2019年5月,谷歌在其官方博客宣布,Flutter 已支持移动、Web、桌面和嵌入式设备,这意味着它正式成为了支持多平台的轻量级 UI 框架。

所以,是时候搞一点新东西了。

Flutter 优势

我简单说一下 Flutter 的优势,首先就是跨平台特性;其次,其采用 Dart 语言,这个语言内部机制决定了 Flutter 有一个很优秀的功能——热重载,也就是说改动代码后不需要重新运行应用(Init…resolve…),代码的改动可以直接作用于正在运行的程序;最后一点就是渲染,他采用 GPU 渲染应用,体验上我完全不输 RN 原生应用。

Flutter 配置

可以参照官方文档进行配置:
https://flutter-io.cn/docs

我踩到的坑:
国内用户提示加的那两个环境变量(两个网站)一定要加在系统变量中,不要加在用户变量,会由于权限问题导致没有效果,导致新建 Flutter 出现卡死情况(网站上不去、解析不了当然卡死)

构建自己的第一个 flutter App(假的)

项目地址:https://github.com/AllenMistake/flutter_app

之所以是假的,是因为完全参考官方给出的示例代码,毕竟,我刚刚上手,API都不懂,怎么码代码嘛,还是要一步一步来,不要一口吃个胖子。

我在这里分析几个源码中我认为比较重点的几个点:

单行函数

1
void main() => runApp(new MyApp());

=>在dart中表示单行函数或方法,算是提高代码简洁度的一个语法糖吧。

widget结构分析

首先我们看一下最初的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: const Text('Welcome to Flutter'),
),
body: const Center(
child: const Text('Hello World'),
),
),
);
}
}

Scaffold 是 Material library 中提供的一个 widget,它提供了默认的导航栏、标题和包含主屏幕 widget 树的 body 属性。widget 树可以很复杂。

一个 widget 的主要工作是提供一个 build() 方法(总是要重写,类似于activity中的 onCreate)来描述如何根据其他较低级别的 widgets 来显示自己。

本示例中的 body 的 widget 树中包含了一个 Center widget,Center widget 又包含一个 Text 子 widget,Center widget 可以将其子 widget 树对其到屏幕中心。

添加依赖

添加依赖在 pubspec.yaml 中,然后点击右上角出现的Packages get,当然别忘了在主程序中引入包。

添加一个 Stateful widget

Stateless widgets 是不可变的,这意味着它们的属性不能改变——所有的值都是 final。

Stateful widgets 持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少需要两个类:1)一个 StatefulWidget 类;2)一个 State 类,StatefulWidget 类本身是不变的,但是 State 类在 widget 生命周期中始终存在。

万物皆是 widget

1
2
3
4
5
6
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);

这个代码中 ListTile 和 Text 都是 widget 他们各自的属性用括号括起来,记住,每个属性写完之后要加逗号,这个很容易忘。

二阶段:加入Icon和跳转页面

我们看一下最终结构关系图

第二阶段结构图
第二阶段结构图

来看看第二阶段学习了哪些内容吧

在 stateful widget 上添加交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Widget _buildRow(WordPair pair) {
final bool 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);
}
});
},
);
}

onTap 属性中,我们添加了点击事件的逻辑。我们在 _buildRow 中让心形 ❤️图标变得可以点击。如果单词条目已经添加到收藏夹中, 再次点击它将其从收藏夹中删除。当心形 ❤️图标被点击时,函数调用 setState() 通知框架状态已经改变。

提示: 在 Flutter 的响应式风格的框架中,调用 setState() 会为 State 对象触发 build() 方法,从而导致对 UI 的更新

导航到第二个页面;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();

return new Scaffold(
appBar: new AppBar(
title: const Text('Saved Suggestion'),
),
body: new ListView(children: divided),
);
},
),
);
}

添加一个显示收藏夹内容的新页面(在 Flutter 中称为路由[route])。

在 Flutter 中,Navigator (导航器)管理应用程序的路由栈。将路由推入(push)到导航器的栈中,将会显示更新为该路由页面。 从导航器的栈中弹出(pop)路由,将显示返回到前一个路由。

我们在 RandomWordsState 的 build 方法中为 AppBar 添加一个列表图标。当用户点击列表图标时,包含收藏夹的新路由页面入栈显示。

添加 Navigator.push 调用,这会使路由入栈(以后路由入栈均指推入到导航管理器的栈)

在新的 route(路由)页面中显示收藏的内容。Navigator(导航器)会在应用栏中自动添加一个”返回”按钮,无需调用Navigator.pop,点击后退按钮就会返回到主页路由。

接下来,添加 MaterialPageRoute 及其 builder。 现在,添加生成 ListTile 行的代码,ListTile 的 divideTiles() 方法在每个 ListTile 之间添加 1 像素的分割线。 该 divided 变量持有最终的列表项,并通过 toList() 方法非常方便的转换成列表显示。

builder 返回一个 Scaffold,其中包含名为”Saved Suggestions”的新路由的应用栏。新路由的body 由包含 ListTiles 行的 ListView 组成;每行之间通过一个分隔线分隔。

最终,我们可以通过 Flutter Inspector 查看 Widget Tree 具体结构

Widget Tree
Widget Tree