前言
首先我们看看官方的定义:
[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
写一段大家很熟悉的代码
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
}
这里有同学好奇,_counter++;
不写在setState
的回调方法里可以生效吗?答案是可以的。但是我更推荐把和状态更新相关的操作,放在回调里面。这样更加的工程化管理。因为一个项目经过长时期的更新,你可能无法分清,这个方法里面的setState
,是不是实际上起作用了,会造成代码冗余。
上面的代码也可以写成这样
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
_counter++;
(context as Element).markNeedsBuild();
}
}
为什么会有同样的作用呢?那我们看看setState
的源码吧
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时,应该注意作用域
举个例子
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
。
我们知道Navigator
是MaterialApp
为我们提供的,但是当前的调用的context
,实际上是MyApp
拥有的实例,并不是MaterialApp
对应的context
。
那我们可以改造一下:
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('跳转到新页面')),
),
);
},
),
);
}
}