Flutter - Getting Started
文章目录

Flutter: Navigator

一般来说, 我们会用页面或者屏幕来形容一个页面中的内容.

Flutter 里面就是用 routes 来形容, 有些类似之前的 Activity

  • Navigator 会保留这些 Route Object 必须提供一套管理方法
  • 对于一些页面较多比较复杂的 APP, 经常会有一个全局的 Navigator 在比较高的层级
  • 虽然这个 Navigator 可以在任何时候新建一个, 但是一些 widget 自己会保留一个 navigator, 可以通过来 Navigator.of 获取并且调用
  • 例如如果新建一个 MaterialApp, 那么当前这个 MA 的 home 就会放到 navigator 的最底下
- 
1
2
3
void main() {
runApp(MaterialApp(home: MyAppHome()));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Navigator.push(context, MaterialPageRoute < void > (
builder: (BuildContext context) {

/* 这个地方返回一个 widget */
return Scaffold(
appBar: AppBar(title: Text('My Page')),
body: Center(
child: FlatButton(
child: Text('POP'),
onPressed: () {
Navigator.pop(context);
},
),
),
);

},
));
1
Navigator.pop(context);

和一些 Widgets 的协作

  • Scaffold 会自动添加一个 Back 按钮到 AppBar , 所以一般不需要实现 Scaffold 的 navigator.pop() , 同时安卓手机上的虚拟按键返回键也有相同的功能

Using named navigator routes

就是给一些 route 一个别名方便之后直接跳转

通过来 MaterialApp 实现:

1
2
3
4
5
6
7
8
9
10
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: < String, WidgetBuilder > {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}

之后又跳转, 就可以通过这样的代码:

1
Navigator.pushNamed(context, '/b');

Routes can return a value

(TODO: 在 push 的时候可以返回数据, 有时候可以进行一些延时操作)

Custom routes

(TODO: route 可以进行自定义, 比如自定义一些淡入淡出的风格之类的)

Flutter: Assets

添加 Assets 资源

其实很简单, 首先我们将资源文件拷贝到项目的目录中

创建一个文件夹 assets , 这个文件夹和 lib 文件夹平级

然后将我们的图片放置到这个文件夹下面的 img 文件夹里面

声明引用 Assets 资源

在文件 pubspec.yaml 里面添加几句:

1
2
assets:
- assets/img/patrickstar.jpg

或者可以直接引用整个文件夹下面的内容

1
2
assets:
- assets/

代码中使用 Asset 资源

1
2
3
4
5
return IconButton(
icon: Image.asset('assets/img/patrickstar.jpg'),
onPressed: null,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);

Flutter: Debug

Dart: Log data

TODO: 到底输出哪个地方?

1
2
3
import 'dart:developer';

log('data: $data');

Flutter: Log data

1
2
3
import 'package:flutter/foundation.dart';

debugPrint('movieTitle: $movieTitle');

Flutter: debugger()

Flutter: Widgets

Flutter widgets are built using a modern react-style framework, which takes inspiration from React. The central idea is that you build your UI out of widgets. Widgets describe what their view should look like given their current configuration and state. When a widget’s state changes, the widget rebuilds its description, which the framework diffs against the previous description in order to determine the minimal changes needed in the underlying render tree to transition from one state to the next.

这个东西和 React 的 Component 类似, 同样有对应的状态管理, 并且会根据之前的状态进行最小化的渲染, 如果状态没有变化, 则不会进行渲染.

最基本的 Flutter App 就是通过 runApp() 方法并且传递一个 widget

1
2
3
4
5
6
7
8
9
10
11
12
import 'package:flutter/material.dart';

void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
  • 通过 runApp() 创建的这个 widget 里面包含两个子 widget, 并且创建了这个会放在 widget tree 的根部
  • 一个 Widget 可能会成为 StatelessWidget 或者 StatefulWidget 的子类, 这就有点像 redux 里面的 Presentational and Container components
  • 一个 widget 的主要工作就是 实现 build 方法 , 这个方法里面定义的第一层级的 widgets 以及渲染逻辑
  • 渲染的时候会根据这个 widget tree 一个接着一个渲染出来, 渲染到表示底层 RenderObject 的 widget 为止.

`

Flutter: setState() method

1
2
3
4
5
@protected
void setState (
VoidCallback fn
)
@protected

这个方法会通知架构当前对象的 internal state 已经改变了

  • 需要传递一个函数
1
setState(() { _myState = newValue });
  • 提供的回调函数会同步执行并且立即执行, 回调函数中不应该有异步的内容(http请求之类的)
  • 这个方法执行的过程中会 build 一次 State 对象
  • 如果是单纯改变了 State 但是没有照这个方法, 那么框架就不会 build UI
  • 不建议在这个方法里面做任何的数据运算

Flutter: Stateful Widget

State_ful_ widgets 持有的状态可能在 widget 生命周期中发生变化, 实现一个 stateful widget 至少需要两个类:

  1. 一个 StatefulWidget 类;
  2. 一个 State 类, StatefulWidget 类本身是不变的, 但是 State 类在 widget 生命周期中始终存在.

StatefulWidget

1
2
3
4
5
class RandomWords extends StatefulWidget {
@override
// RandomWordsState 是一个状态类
RandomWordsState createState() => new RandomWordsState();
}

State

1
2
3
4
5
6
7
8
9
10
11
12
// 注意extend 后方的内容: State<RandomWords>表明我们在使用专门用于 RandomWords 的 State 泛型类, 保存应用的逻辑和状态,并且会维护 RandomWords 的控件的状态.
class RandomWordsState extends State<RandomWords> {
@override
// 这个状态类,实际上只是返回了一段随意的文本
Widget build(BuildContext context) {
final WordPair wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
```# Flutter: 添加新的三方库

在 `pubspec.yaml` 中,将 `需要添加的库` 添加到依赖项列表,如下面高亮显示的行:

dependencies:
flutter:

sdk: flutter

cupertino_icons: ^0.1.0
english_words: ^3.1.0 # 新增了这一行

1
2
3
4

然后跑到 VSC 或者 AS 里面 点击右上角的 `Packages get` 按钮下载对应的包.

然后在十几天代码中需要 import:

import ‘package:flutter/material.dart’;
import ‘package:english_words/english_words.dart’; // 新增了这一行

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43

>如果添加的这个库,在下方的代码中没有被用到,那么这一行会弹出灰色的提示

# Dart: Create Simple Class

## A simple class: Bicycle

```java
class Bicycle {
int cadence;
int _speed = 0;
int gear;

int get speed => _speed;

Bicycle(this.cadence, this.gear);

void applyBrake(int decrement) {
_speed -= decrement;
}

void speedUp(int increment) {
_speed += increment;
}

@override
String toString() => 'Bicycle: $_speed mph';
}

void main() {
var bike = Bicycle(3, 2);
print(bike);

print(bike.cadence);

print(bike.gear);
bike.gear = 10;
print(bike.gear);

print(bike.speed);
bike._speed = 10;
print(bike._speed);
}

几个要点:

  • 每一个 dart 文件都可以当做是一个
    • 引用其他库的方法:
1
2
3
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';

这里的第一行引用了一个 dart 标准的输入输出库, 第二第三行引用了自定义的库

  • 在变量名前增加下划线 _ 来标记为它是私有的
    • 这里的私有的概念和 JAVA 的不同, 其他库中将无法访问当前库中定义的私有变量, 因此在当前库中下方的 main() 方法依然可以使用这些数据
1
int _speed = 0;
  • 仅仅使用一行即可代替复杂的构造函数:
1
Bicycle(this.cadence, this.gear);

这一行可以代替下方代码:

1
2
3
4
5
Bicycle(int cadence, int speed, int gear) {
this.cadence = cadence;
this.speed = speed;
this.gear = gear;
}

Rectangle Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import 'dart:math';

class Rectangle {
int width;
int height;
Point origin;

Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});

@override
String toString() =>
'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';
}

main() {
print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
print(Rectangle(origin: const Point(10, 10)));
print(Rectangle(width: 200));
print(Rectangle());
}

几个要点:

  • this.origin , this.widththis.height 使用了 Dart 提供的简便方法来直接对类中的实例变量进行赋值.
1
Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});
  • this.origin , this.widththis.height 嵌套在闭合的花括号中 ( {} ) , 用来表示它们是可选的命名参数.
  • this.origin = const Point(0, 0) 这样的代码表明给实例变量 origin 提供了默认的值 Point(0,0) , 默认值必须是在编译器就可以确定的常量. 上述代码中的构造方法为三个实例变量都提供了默认参数.
  • 函数的定义可以变得十分简单:
1
String scream(int length) => "A${'a' * length}h!";

Factory Pattern

Traditional Factory Pattern

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
30
import 'dart:math';

abstract class Shape {
num get area;
}

class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}

class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}

Shape shapeFactory(String type, num r) {
if (type == 'circle') return Circle(r);
if (type == 'square') return Square(r);
throw 'Can\'t create $type.';
}

main() {
final circle = shapeFactory('circle', 2);
final square = shapeFactory('square', 3);
print(circle.area);
print(square.area);
}
  • import 'dart:math' 表示 import 一个 Dart 核心库, 其余的还有诸如 dart:core, dart:async, dart:convert 和 dart:collection 这样的核心库

之后就可以直接使用这个库里面的变量 pi :

1
num get area => pi * pow(radius, 2);
  • 这是一个抽象类:
1
2
3
abstract class Shape {
num get area;
}

定义之后会在实际实现的类里面进行方法的定义
如果后续 实现这个抽象类的 class 没有对这个方法进行定义的话就会报错
错误示例:

1
Error: The non-abstract class 'Circle' is missing implementations for these members:
  • 还有另外一种工厂模式的解决方案

Modern Dart Factory Pattern

和上方工厂模式不同的地方, 就在于我们 shapeFactory() 函数改为放到一开始的抽象类里面, 并且使用 factory 关键字

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
import 'dart:math';

abstract class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
num get area;
}

class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}

class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}

main() {
final circle = Shape('circle');
final square = Shape('square');
print(circle.area);
print(square.area);
}

Interface

Dart 语言并没有提供 interface 关键字, 但是每一个类都隐式地定义了一个接口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 'dart:math';

class Circle {
final num radius;
num get area => pi * pow(radius, 2);

Circle(this.radius);
}

class CircleMock implements Circle {
num area;
num radius;
}

main() {
print(123);
}
  • CircleMock 这个类扩展了 Circle 类, 并且必须要提供两个 attr 否则会报错