前言 首先我们看看官方的定义:
[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。 我们知道Navigator是MaterialApp为我们提供的,但是当前的调用的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('跳转到新页面')), ), ); }, ), ); } }