Flutter中Widget BuildContext与Builder BuildContext的区别及问题排查
嘿,这个问题我当初刚接触Flutter时也踩过一模一样的坑,咱们把它拆解清楚~
核心原理:BuildContext是什么?
每个Widget对应的BuildContext本质上是它在Widget树中的位置标记。Scaffold.of(context)这个方法的作用是:从当前context所在的位置开始,向上遍历Widget树,找到最近的Scaffold实例。
为什么你的代码里两种Context表现不同?
咱们先看你的代码结构:
class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { // 这个context是_MyHomePageState对应的上下文 return new MaterialApp( // 这里其实没必要嵌套MaterialApp,后面说原因 home: new Scaffold( // ... 子Widget树 child: new Builder( builder: (BuildContext context){ // 这个context是Builder组件的上下文 return RaisedButton( onPressed: (){ Scaffold.of(context).showSnackBar(...) } ) } ) ) ); } }
1. 组件build方法的Context为什么找不到Scaffold?
build(BuildContext context)里的context属于_MyHomePageState对应的Widget层级,它在Widget树中的位置是:MyApp -> MaterialApp -> MyHomePage -> [这个context的位置]
而你创建的Scaffold是在_MyHomePageState的build方法内部返回的,相当于在这个context的下层。当你调用Scaffold.of(context)时,会向上遍历,自然找不到下层的Scaffold,所以报错。
另外,你的代码里还有个小问题:MyHomePage的build方法里不需要再嵌套MaterialApp——一个Flutter应用只需要一个MaterialApp作为根即可,多余的嵌套会导致Widget树层级混乱,建议直接返回Scaffold。
2. Builder组件的Context为什么能正常工作?
Builder是一个轻量Widget,它的核心作用就是提供一个新的BuildContext。这个新的context属于Builder自身,而Builder是放在Scaffold的body子树里的,它在Widget树中的位置是:Scaffold -> Column -> Form -> Row -> Container -> Builder -> [这个context的位置]
当调用Scaffold.of(context)时,向上遍历就能找到上层的Scaffold实例,所以SnackBar能正常显示。
两种Context的本质区别
- 组件build方法的Context:是当前组件在Widget树中的上下文,它的层级高于该组件
build方法内部创建的所有子Widget,因此无法访问这些子Widget中的InheritedWidget(比如Scaffold)。 - Builder的Context:是Builder组件的上下文,它属于当前组件
build方法内部子树的一部分,层级低于上层的Scaffold,因此可以向上查找到对应的Scaffold实例。
其他解决方案
除了用Builder,你还可以通过给Scaffold设置GlobalKey来直接获取实例,这样不需要依赖Context:
class _MyHomePageState extends State<MyHomePage> { final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); final formKey = new GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, // 给Scaffold设置key body: new Column( // ... 你的原有子Widget结构 children: [ // ... new Container( child: RaisedButton( child: new Text("Sign in"), onPressed: (){ if (formKey.currentState.validate()) { _scaffoldKey.currentState.showSnackBar( new SnackBar(content: new Text('Processing Data')) ); } }, ), ), ] ), ); } }
内容的提问来源于stack exchange,提问作者Mohammad Tabbara

