Flutter如何实现跨页面持久显示Flushbar全局通知?
嘿,我之前也碰到过这个问题——用Flushbar或者Snackbar的时候,页面一跳转通知就跟着没了,特烦!要实现不受页面导航影响的全局通知,核心就是让通知挂载在应用的根上下文上,而不是单个页面的组件树里。下面给你两个可行的方案,其中第一个就是你提到的George的方案,亲测好用!
方案一:用Root Navigator的全局上下文(最推荐)
这个方案最简单,只需要给MaterialApp绑定一个全局的navigatorKey,然后用它来获取根上下文显示Flushbar,这样通知就和页面导航彻底解绑了。
步骤1:给MaterialApp加全局navigatorKey
修改你的MyApp代码,加一个静态的navigatorKey:
class MyApp extends StatelessWidget { // 全局导航Key,用来获取根上下文 static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: navigatorKey, // 把Key绑定到MaterialApp上 home: FirstScreen(), ); } }
步骤2:修改Flushbar的显示方法
之前你传的是当前页面的context,现在改成用全局的根上下文,而且不用再手动传context了:
Future showFlushbar(Flushbar instance) { // 从全局Key获取根上下文 final rootContext = MyApp.navigatorKey.currentContext!; final _route = route.showFlushbar( context: rootContext, flushbar: instance, ); // 用rootNavigator:true确保用的是根导航器 return Navigator.of(rootContext, rootNavigator: true).push(_route); }
然后在SecondScreen里调用的时候,直接传Flushbar实例就行,不用带context:
onPressed: () { showFlushbar(_flushbar); },
这样改完之后,你从SecondScreen返回FirstScreen,Flushbar会稳稳地留在屏幕顶部,直到它自己消失或者被关掉~
方案二:用Overlay手动实现全局挂载(进阶备选)
你提到Overlay方案不够优雅,确实是这样——因为Flushbar本身已经封装了路由和动画,用Overlay的话得自己写这些逻辑,但如果以后你需要自定义更复杂的全局组件,这个思路还是有用的。
核心就是在应用最顶层的Overlay里插入一个自定义的通知组件,代码大概是这样:
class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { OverlayEntry? _notificationEntry; // 显示全局通知的方法 void showGlobalNotification(String message) { if (_notificationEntry != null) return; // 避免重复显示 _notificationEntry = OverlayEntry( builder: (context) => Positioned( top: 50, // 距离顶部的位置,对应状态栏下面 left: 20, right: 20, child: Material( elevation: 8, borderRadius: BorderRadius.circular(8), child: Container( padding: EdgeInsets.all(16), child: Row( children: [ Expanded(child: Text(message)), IconButton( icon: Icon(Icons.close), onPressed: hideGlobalNotification, ) ], ), ), ), ), ); // 把通知插入到全局Overlay里 Overlay.of(context)!.insert(_notificationEntry!); // 3秒后自动隐藏 Future.delayed(Duration(seconds: 3), hideGlobalNotification); } // 隐藏通知的方法 void hideGlobalNotification() { _notificationEntry?.remove(); setState(() => _notificationEntry = null); } @override Widget build(BuildContext context) { return MaterialApp( home: FirstScreen( showNotification: showGlobalNotification, // 把方法传给子页面调用 ), ); } }
不过这个方案需要自己处理动画、点击关闭这些细节,确实不如Flushbar现成的方便,所以如果只是用Flushbar的话,方案一绝对是首选。
最后验证一下
改完方案一的代码后,你在SecondScreen点“show flushbar”,然后返回FirstScreen,就能看到通知还好好地显示在屏幕上啦!
内容的提问来源于stack exchange,提问作者chris

