DartNotes

| visited times

A shorthand => (arrow) syntax

  1. contain a single statement(一条语句)
  2. especially useful when passing anonymous functions as arguments
    1
    flybyObjects.where((name) => name.contains('turn')).forEach(print);

箭头函数:

  1. forEach
    1
    2
    3
    4
    5
    6
    7
    List l1 = [1, 2];

    l1.forEach((item) {
    print(item);
    });

    l1.forEach((item) => print(item));
  1. map
    1
    2
    3
    4
    5
    6
    7
    var l2 = l1.map((item) {
    return item % 2 == 0 ? item * 2 : item;
    });
    print(l2.toList());

    var l3 = l1.map((item) => item % 2 == 0 ? item * 2 : item);
    print(l3);

检查Null或零

  1. 只有bool值为“true”才被视为true;
  2. ?.:运算符在左边为null的情况下会阻断右边的调用;
  3. ??:在左边表达式为null时为其设置默认值;
  4. searchModel?.data?.length ?? 0:获取一个对象中数组长度

const

  1. 类是对象的模板,对象是类的实例
  2. static: 所修饰的变量是属于类的,而不是属于对象的; static变量直到运行期被使用时才会实例化
  3. const:
    const 定义时,需要是个明确的值,不能像 final 那样,运行时才知道是什么值;
    被const修饰的对象有些特殊的属性和限制:
    ①必须依靠编译期间就能够计算出来的数据进行创建,包括两种情况:
    1)使用Dart内置数据类型的值(int double bool String List Map等等)进行赋值,或者使用内置数据类型的字面量通过基本运算得到的值,而不能依赖运行期计算出来的值,但是new DateTime.now()不行

    2)const构造函数创建的对象
    这里的Student类中定义了一个const构造函数,一个类能够定义 const 构造函数的前提是成员变量必须都是用final或const修饰的
    ②const导致的不可变性是可传递的


    ③相同的const变量不会在内存中重复创建,如果表达式被调用了多次,则重用之前创建好的常量,或者用代码表达这一特性
    1
    2
    3
    4
    5
    6
    7
    getConst() => const [1, 2];
    main() {
    var a = getConst();
    var b = getConst();
    identical(a, b); // =>true
    }
    //identical用于检查两个引用是否指向同一个对象。

super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person {
int age;
string name;
Person(int age, String name){
this.age = age;
this.name = name;
}
void sayhi(){
print('my name is' this.name)
}
}

class Worker extend Person{
int salary;
Worker(int age, string name, int salary) : super(age, name){  //super表示要继承父类的属性
this.salary = salary;
}
@override  //告诉编译器和程序员他是一个重写覆盖父类的方法
void sayhi(){     super.sayhi()    //表示调用父类的sayhi方法
print('my salary is'+ this.salary)
}
}

key

Keys preserve state when widgets move around in your widget tree. They can be used to preserve the user’s scroll location, or keeping state when modifying a collection. 当组件在组件树中移动时使用Key可以保持组件之前的状态,比如在用户滑动时或者集合改变时。

当使用Stateless Widget时,我们并不需要使用key,当使用Stateful Widget时,集合内有数据移动和改变并且需要展示到界面时才需要key

//TODO

BuildContext

BuildContext objects are actually Element objects. The BuildContext interface is used to discourage direct manipulation of Element objects.

视图树装载过程

StatelessWidget

  1. 首先它会调用StatelessWidget的 createElement 方法,并根据这个widget生成StatelessElement对象。
  2. 将这个StatelessElement对象挂载到element树上。
  3. StatelessElement对象调用widget的build方法,并将element自身作为BuildContext传入。

    StatefulWidget

  4. 首先同样也是调用StatefulWidget的 createElement方法,并根据这个widget生成StatefulElement对象,并保留widget引用。
  5. 将这个StatefulElement挂载到Element树上。
  6. 根据widget的 createState 方法创建State。
  7. StatefulElement对象调用state的build方法,并将element自身作为BuildContext传入。

所以我们在build函数中所使用的context,正是当前widget所创建的Element对象。

of(context)方法

1
2
3
4
5
6
//打开一个新的页面
Navigator.of(context).push
//打开Scaffold的Drawer
Scaffold.of(context).openDrawer
//获取display1样式文字主题
Theme.of(context).textTheme.display1

以Navigator打开新页面为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false,
}) {
//关键代码-----------------------------------------v

final NavigatorState navigator = rootNavigator
? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())
: context.ancestorStateOfType(const TypeMatcher<NavigatorState>());

//关键代码----------------------------------------^
assert(() {
if (navigator == null && !nullOk) {
throw FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'The context used to push or pop routes from the Navigator must be that of a '
'widget that is a descendant of a Navigator widget.'
);
}
return true;
}());
return navigator;
}

可以看到,关键代码部分通过context.rootAncestorStateOfType向上遍历 Element tree,并找到最近匹配的 NavigatorState。也就是说of实际上是对context跨组件获取数据的一个封装。

而我们的Navigator的 push操作就是通过找到的 NavigatorState 来完成的。

需要注意的是,在 State 中 initState阶段是无法跨组件拿数据的,只有在didChangeDependencies之后才可以使用这些方法.

WidgetsFlutterBinding

Mixins

Mixin 能够更好的解决多继承中容易出现的问题,如:方法优先顺序混乱、参数冲突、类结构变得复杂化等

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
44
45
46
class A {
a() {
print("A.a()");
}

b() {
print("A.b()");
}
}

class A2 {
a() {
print("A2.a()");
}
}

class B {
a() {
print("B.a()");
}

b() {
print("B.b()");
}

c() {
print("B.c()");
}
}

class G extends B with A, A2 {

}


testMixins() {
G t = new G();
t.a();
t.b();
t.c();
}

/// ***********************输出***********************
///I/flutter (13627): A2.a()
///I/flutter (13627): A.b()
///I/flutter (13627): B.c()

在 Dart 中 with 就是用于 mixins。
可以看出,class G extends B with A, A2 ,在执行 G 的 a、b、c 方法后,输出了 A2.a()、A.b() 、B.c()
所以结论上简单来说,就是相同方法被覆盖了,并且 with 后面的会覆盖前面的。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

abstract class Base {
a() {
print("base a()");
}

b() {
print("base b()");
}

c() {
print("base c()");
}
}

class A extends Base {
a() {
print("A.a()");
super.a();
}

b() {
print("A.b()");
super.b();
}
}

class A2 extends Base {
a() {
print("A2.a()");
super.a();
}
}

class B extends Base {
a() {
print("B.a()");
super.a();
}

b() {
print("B.b()");
super.b();
}

c() {
print("B.c()");
super.c();
}
}

class G extends B with A, A2 {

}

testMixins() {
G t = new G();
t.a();
t.b();
t.c();
}

///I/flutter (13627): A2.a()
///I/flutter (13627): A.a()
///I/flutter (13627): B.a()
///I/flutter (13627): base a()
///I/flutter (13627): A.b()
///I/flutter (13627): B.b()
///I/flutter (13627): base b()
///I/flutter (13627): B.c()
///I/flutter (13627): base c()

A、A2、B中的所有方法都被执行了,且只执行了一次,同时执行的顺序也是和 with 的顺序有关
如果把上方代码中 class A.a() 方法的 super 去掉,那么你将看不到 B.a() 和 base a() 的输出。

WidgetsFlutterBinding 在 Flutter启动时runApp会被调用,作为App的入口,它肯定需要承担各类的初始化以及功能配置,这种情况下,Mixins 的作用就体现出来了。

map

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
return Column(
children: <Widget>[
Container(
key: Key('1'),
color: Colors.red,
),
Container(
color: Colors.blue,
),
Container(
color: Colors.grey,
),
]
//.map((Widget widget){ 也是可以的
.map<Widget>((Widget widget){
print(widget.key);
int flex = 1;
if (widget.key == Key('1')) {
flex = 2;
}
return Expanded(
flex: flex,
child: widget,
);
})
.toList(),
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
return Column(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.red,
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.blue,
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.grey,
),
),
],
);

###.. Operator

1
2
3
4
5
6
7
StringBuffer valueBuffer = new StringBuffer();
valueBuffer..write("I")
..write(" ")
..write("love")
..write(" ")
..write("Flutter");
print(valueBuffer.toString());