cubegao

Flutter中的BuildContext到底是什么

2021-03-11

前言

首先我们看看官方的定义:

[BuildContext] objects are actually [Element] objects. The [BuildContext]
interface is used to discourage direct manipulation of [Element] objects.
[BuildContext] 对象实际上是 [Element] 对象。 [BuildContext] 接口用于阻止直接操作 [Element] 对象。

根据官方这段注释,可以了解到BuildContext 实际上就是 Element 对象,产生的意义主要是为了防止开发者直接操作 Element 对象。

如何使用BuildContext

写一段大家很熟悉的代码

1
2
3
4
5
6
7
8
9
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}
}

这里有同学好奇,_counter++;不写在setState的回调方法里可以生效吗?答案是可以的。但是我更推荐把和状态更新相关的操作,放在回调里面。这样更加的工程化管理。因为一个项目经过长时期的更新,你可能无法分清,这个方法里面的setState,是不是实际上起作用了,会造成代码冗余。

上面的代码也可以写成这样

1
2
3
4
5
6
7
8
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
_counter++;
(context as Element).markNeedsBuild();
}
}

为什么会有同样的作用呢?那我们看看setState的源码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setState(VoidCallback fn) {
assert(fn != null);
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
///......
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
///......
}
return true;
}());
final dynamic result = fn() as dynamic;
assert(() {
if (result is Future) {
///......
]);
}
return true;
}());
_element!.markNeedsBuild();
}

那么我们就清楚了,BuildContext实际上就是Widget树中特定位置所对应的实例Element

使用BuildContext时,应该注意作用域

举个例子

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
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context){
return MaterialApp(
title: 'Test Flutter',
home: Scaffold(
body: Center(
child: FlatButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewWidget()));
},
child: Text('跳转到新页面')),
),
),
);
}
}
class NewWidget extends StatelessWidget {
@override
Widget build (BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text("this is title"),
),
body: Center(
child: Text("this is body"),
),
);
}
}

运行一下,发现会报错:提示当前的context找不到Navigator
我们知道NavigatorMaterialApp为我们提供的,但是当前的调用的context,实际上是MyApp拥有的实例,并不是MaterialApp对应的context
那我们可以改造一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context){
return MaterialApp(
title: 'Test Flutter',
home: Scaffold(
body: Center(
child: Builder(
builder: (context) {
return FlatButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewWidget()));
},
child: Text('跳转到新页面')),
),
);
},
),
);
}
}

扫描二维码,分享此文章