Flutter中showDialog内调用Navigator.push报找不到Provider<UserProvider>错误
错误触发原因
核心为BuildContext的作用域不匹配,具体逻辑如下:
- 页面按钮点击时调用
Navigator.push使用的是当前页面的context,该上下文处于UserProvider的注册作用域内,跳转生成的新页面可以通过该上下文正常访问到上层的UserProvider。 showDialog生成的弹窗是Navigator维护的独立路由层,showDialog的builder回调传入的context是弹窗专属的路由上下文,不在原有UserProvider的作用域范围内。你在弹窗点击事件中直接使用该弹窗上下文执行跳转,新页面的祖先链路中不存在UserProvider,因此触发找不到Provider的错误。
解决方案
以下三种方案按需选择即可:
方案1:调整context引用(最便捷,无需调整Provider注册逻辑)
重命名showDialog的builder参数的上下文变量名,避免覆盖父页面的context,跳转时使用父页面的上下文,关闭弹窗时使用弹窗自身的上下文即可,修改后的代码参考:
query_popup() { return showDialog( context: context, // 此处用的是父页面的context // 将builder的参数重命名为dialogContext,避免和父级context重名冲突 builder: (BuildContext dialogContext) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8.0)), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Container(height: 5.0), Text("End & save?"), Container(height: 20.0), InkWell( onTap: () { // 跳转时使用父页面的context,保证Provider作用域正常 Navigator.push(context,MaterialPageRoute(builder: (context) => TabbarScreen(SelectedPage: 0)),); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container(width: 10.0, height: 40.0), Text('Yes'), ], ), ), Container(height: 5.0), InkWell( onTap: () { // 关闭弹窗用弹窗自身的context Navigator.pop(dialogContext); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container(width: 20.0, height: 40.0), Text('No') ], ), ), ], ), ); }, ); }
方案2:提升Provider注册层级(适合全局使用的Provider)
如果UserProvider是你整个应用都需要用到的全局状态,直接将它的注册逻辑提升到MaterialApp的父层级,这样所有路由(包括弹窗、新打开的页面)都可以正常访问到该Provider:
void main() { runApp( ChangeNotifierProvider( create: (context) => UserProvider(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // 原有应用配置不变 ); } }
方案3:向弹窗透传Provider(适合弹窗内部需要直接访问Provider的场景)
如果你的弹窗内部逻辑本身也需要使用UserProvider,可以在弹窗的builder中将父级的Provider透传到弹窗的组件树内:
query_popup() { return showDialog( context: context, builder: (BuildContext context) { return ChangeNotifierProvider.value( // 透传父级的UserProvider value: Provider.of<UserProvider>(context, listen: false), child: AlertDialog( // 原有弹窗内容不变 ), ); }, ); }
内容的提问来源于stack exchange,提问作者Bernhard
相关产品推荐
相关产品推荐

