Flutter路由切换后MaterialBanner无法关闭,报查询已停用组件祖先不安全错误
问题原因
- 你先调用
Navigator.of(context).pop()将RecoverScreen出栈后,RecoverScreen对应的context已经被标记为停用状态,后续无论是弹出Banner还是点击关闭按钮时,再通过这个已失效的context调用ScaffoldMessenger.of(context)查找祖先节点,就会触发报错。 - MaterialBanner的关闭回调绑定的是RecoverScreen的失效上下文,点击时自然无法正常查找到ScaffoldMessenger实例执行隐藏操作。
解决方案
最优方案是提前持有上层全局的ScaffoldMessenger实例,避免依赖已销毁页面的context:
- 在执行页面pop操作前,先把当前context对应的ScaffoldMessenger实例保存下来
- 后续弹出Banner、隐藏Banner都直接使用这个提前保存的实例,不需要再通过context查找
修改后的核心代码如下:
// RecoverScreen中发送按钮的onPressed逻辑 onPressed: () { // 提前保存ScaffoldMessenger实例,避免后续使用失效的context final scaffoldMessenger = ScaffoldMessenger.of(context); // 执行页面返回操作 Navigator.of(context).pop(); // 用提前保存的实例弹出MaterialBanner scaffoldMessenger.showMaterialBanner( MaterialBanner( padding: const EdgeInsets.all(20), content: const Text('Recover password email was sended.'), leading: const Icon(Icons.info), backgroundColor: Colors.green, actions: <Widget>[ TextButton( child: const Text( 'Cerrar', style: TextStyle(color: Colors.white), ), onPressed: () { // 同样用提前保存的实例隐藏Banner scaffoldMessenger.hideCurrentMaterialBanner(); }, ), ], ), ); },
原理说明
Flutter中每个组件的context和其生命周期绑定,页面被pop销毁后,对应的context就不再可用,无法通过它查找祖先组件。提前保存ScaffoldMessenger的实例引用,绕开了后续对失效context的依赖,因此可以正常执行弹出和隐藏操作。
可选方案:如果业务逻辑允许,也可以通过路由传递回调函数的方式,让LoginScreen负责Banner的弹出和隐藏,所有操作都使用LoginScreen的有效上下文,也能避免该问题。
内容的提问来源于stack exchange,提问作者Alexis enp
相关产品推荐
相关产品推荐

