这篇文章是为那些想将已有的 iOS 开发经验运用到 Flutter 开发中的 iOS 开发者所作。 如果你理解 iOS framework 的基本原理,那么你可以将这篇文章作为学习 Flutter 开发的起点。
本系列上部分:
本文结构如下:
1. Views(上)
2. 导航(上)
3. 线程和异步(上)
4. 工程结构、本地化、依赖和资源(上)
5. ViewControllers(下)
6. 布局(下)
7. 手势检测与 touch 事件处理(下)
8. 主题和文字(下)
9. 表单输入(下)
10. 和硬件、第三方服务以及系统平台交互(下)
11. 数据库和本地存储(下)
12. 通知(下)
五、ViewControllers
5.1 ViewController 相当于 Flutter 中的什么?
在 iOS 里,一个 ViewController 是用户界面的一部分,通常是作为屏幕或者其中的一部分来使用。 这些组合在一起构成了复杂的用户界面,并以此对应用的 UI 做不断的扩充。 在 Flutter 中,这一任务又落到了 Widget 这里。就像在导航那一章提到的, Flutter 中的屏幕也是使用 Widgets 表示的,因为“万物皆 widget!”。使用 Naivgator
在不同的 Route
之间切换,而不同的路由则代表了不同的屏幕或页面,或是不同的状态,也可能是渲染相同的数据。
5.2 如何监听 iOS 中的生命周期?
在 iOS 里,可以重写 ViewController
的方法来捕获自身的生命周期,或者在 AppDelegate
中注册生命 周期的回调。Flutter 中则没有这两个概念,但是你可以通过在 WidgetsBinding
的 observer 中挂钩子,也可以 通过监听didChangeAppLifecycleState()
事件,来实现相应的功能。
可监听的生命周期事件有:
inactive
- 应用当前处于不活跃状态,不接收用户输入事件。这个事件只在 iOS 上有效,Android 中没有类似的状态。paused
- 应用处于用户不可见状态,不接收用户输入事件,但仍在后台运行。resumed
- 应用可见,也响应用户输入。suspending
- 应用被挂起,在 iOS 平台没有这一事件。
更多细节,请参见 AppLifecycleStatus
文档。
六、布局
6.1 UITableView 和 UICollectionView 相当于 Flutter 中的什么?
在 iOS 里,你可能使用 UITableView
或者 UICollectionView
来展示一个列表。而在 Flutter 里,你可以使用 ListView 来达到类似的实现。在 iOS 中,你通过 delegate 方法来确定显示的行数,相应位置的 cell,以及 cell 的尺寸。
由于 Flutter 中 widget 的不可变特性,你需要向 ListView
传递一个 widget 列表,Flutter 会确保滚动快速而流畅。
1import 'package:flutter/material.dart';
2
3void main() {
4 runApp(SampleApp());
5}
6
7class SampleApp extends StatelessWidget {
8 // This widget is the root of your application.
9 @override
10 Widget build(BuildContext context) {
11 return MaterialApp(
12 title: 'Sample App',
13 theme: ThemeData(
14 primarySwatch: Colors.blue,
15 ),
16 home: SampleAppPage(),
17 );
18 }
19}
20
21class SampleAppPage extends StatefulWidget {
22 SampleAppPage({Key key}) : super(key: key);
23
24 @override
25 _SampleAppPageState createState() => _SampleAppPageState();
26}
27
28class _SampleAppPageState extends State<SampleAppPage> {
29 @override
30 Widget build(BuildContext context) {
31 return Scaffold(
32 appBar: AppBar(
33 title: Text("Sample App"),
34 ),
35 body: ListView(children: _getListData()),
36 );
37 }
38
39 _getListData() {
40 List<Widget> widgets = [];
41 for (int i = 0; i < 100; i++) {
42 widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i")));
43 }
44 return widgets;
45 }
46}
6.2 如何确定列表中被点击的元素?
在 iOS 中,tableView:didSelectRowAtIndexPath:
代理方法可以用来实现该功能。而在 Flutter 中,需要通过 widget 传递进来的 touch 响应处理来实现。
1import 'package:flutter/material.dart';
2
3void main() {
4 runApp(SampleApp());
5}
6
7class SampleApp extends StatelessWidget {
8 // This widget is the root of your application.
9 @override
10 Widget build(BuildContext context) {
11 return MaterialApp(
12 title: 'Sample App',
13 theme: ThemeData(
14 primarySwatch: Colors.blue,
15 ),
16 home: SampleAppPage(),
17 );
18 }
19}
20
21class SampleAppPage extends StatefulWidget {
22 SampleAppPage({Key key}) : super(key: key);
23
24 @override
25 _SampleAppPageState createState() => _SampleAppPageState();
26}
27
28class _SampleAppPageState extends State<SampleAppPage> {
29 @override
30 Widget build(BuildContext context) {
31 return Scaffold(
32 appBar: AppBar(
33 title: Text("Sample App"),
34 ),
35 body: ListView(children: _getListData()),
36 );
37 }
38
39 _getListData() {
40 List<Widget> widgets = [];
41 for (int i = 0; i < 100; i++) {
42 widgets.add(GestureDetector(
43 child: Padding(
44 padding: EdgeInsets.all(10.0),
45 child: Text("Row $i"),
46 ),
47 onTap: () {
48 print('row tapped');
49 },
50 ));
51 }
52 return widgets;
53 }
54}
6.3 如何动态更新?
在 iOS 中,可以更新列表数据,调用 reloadData
方法通知 tableView 或 collectionView。
在 Flutter 里,如果你在 setState()
中更新了 widget 列表,你会发现展示的数据并不会立刻更新。这是因为当