Flutter路由跳转新页面后无法访问Provider问题求助
这个问题我之前也碰到过,核心原因是你的Provider层级放错位置了!
看你的代码,ChangeNotifierProvider是放在MaterialApp的home属性里,但MaterialApp内部会自动创建一个Navigator,也就是说你的Provider是在这个Navigator的子树里。当你用Navigator.push打开Page2时,Page2是被这个Navigator管理的,它的上下文只能向上找到Navigator,而Navigator的上层并没有Provider,自然就找不到了。
下面给你几种靠谱的解决方案,按推荐程度排序:
方案1:把Provider提升到MaterialApp外面(最推荐)
直接把ChangeNotifierProvider包裹在MaterialApp外层,这样整个App的所有组件(包括Navigator管理的所有页面)都能访问到Provider。修改后的代码如下:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( // 移到MaterialApp外面 create: (context) => MyModel(), child: MaterialApp( title: 'Flutter Demo', home: MyHomePage() ) ); } }
这样修改后,Page2里的Provider.of<MyModel>(context)就能正常找到实例了,完全不需要改动其他代码。
方案2:利用MaterialApp的builder属性调整层级
如果你不想把Provider放在最外层,也可以用MaterialApp的builder属性来让Navigator处于Provider的子树中:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', builder: (context, child) { return ChangeNotifierProvider( create: (context) => MyModel(), child: child, ); }, home: MyHomePage() ); } }
builder属性返回的Widget会包裹在MaterialApp的Navigator外面,所以Provider的层级就比Navigator高,所有通过Navigator打开的页面都能访问到。
方案3:在跳转时传递Provider实例(不推荐,耦合性高)
虽然不建议,但如果特殊场景下你不想调整Provider层级,可以在push的时候把MyModel实例传过去:
在MyHomePage的FloatingActionButton点击事件里修改:
onPressed: () { var myModel = Provider.of<MyModel>(context, listen: false); Navigator.of(context).push( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => Page2(myModel: myModel), ), ); }
然后修改Page2的定义:
class Page2 extends StatelessWidget { final MyModel myModel; Page2({required this.myModel}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(myModel.b), ), ); } }
这种方式虽然能解决问题,但会让Page2和MyModel产生强耦合,违背了Provider的依赖注入思想,所以只适合临时救急,不推荐长期使用。
另外你提到的Navigator.of(context, rootNavigator: true).context没用,是因为rootNavigator还是MaterialApp创建的那个Navigator,它的上下文上层依然没有Provider,所以自然找不到。
内容的提问来源于stack exchange,提问作者user2233706

