show snackbar on fcm notification on every screen
Solution 1
The problem is with registering scaffolds for your NotificationManager widget since every time a new scaffold is added to the stack for a new screen, you need to register that screen's scaffold in the NotificationManager. This is because the line:
Scaffold.of(_context).showSnackBar(snackBar);
in your NoticicationManager will only look up the widget tree until the first scaffold it finds and call it there. Since you call NotificationManger.init(context: context);
in your HomeScreen widget and pass the context of the HomeScreen, it will only live inside that scaffold. So, if you navigate away from the HomeScreen to a new widget with a different scaffold it will not have the NotificationManager as a child.
To fix the issue be sure you call Fcm.initConfigure();
in the first page that loads for the app, and for any pages you navigate to call NotificationManger.init(context: context);
in either the initState() method for stateful widgets to register the current scaffold of that page or if they are stateless widgets, you can add it in the build method before returning the scaffold.
Solution 2
Copying the same code on all screens may not be ideal. If you have to change something, you will have to change everything or you will see inconsistencies. For snackbars, you can use this library and solve your problem in literally 1 line of code:
on your onMessage method:
onMessage(message){
Get.snackbar("message", message); // this line
}
Yes, the snackbar will be displayed regardless of the screen the user is on, and you won't need the scaffold's context, nor any context, just call the library method and that's it.
lib: https://pub.dev/packages/get
Comments
-
BeHappy over 1 year
I want to show snackbar when in app notification arrive. But when I configure firebase on first screen, snackbar shows only when user is on that screen. I try to create a class to get BuildContext and show snackbar based on it but doesn't work and not show snackbar.
This is my HomeScreen.dart:class _HomeScreenState extends State<HomeScreen> { @override void initState() { super.initState(); Future.delayed(Duration.zero, () { NotificationManger.init(context: context); Fcm.initConfigure(); }); } @override Widget build(BuildContext context) { return StoreConnector<AppState, Store<AppState>>( converter: (store) => store, onInit: (store) => initApp(store), builder: (context, store) { return BlocProvider<HomeBloc>( create: (context) { return HomeBloc(homeRepository: homeRepository)..add(ScreenOpened()); }, child: BlocListener<HomeBloc, HomeState>( listener: (context, state) async {}, child: BlocBuilder<HomeBloc, HomeState>( builder: (context, state) { return Scaffold( key: _scaffoldKey, ... ); }, ), ), ); }, ); } }
This is my Fcm.dart
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) { if (message.containsKey('data')) { final dynamic data = message['data']; } if (message.containsKey('notification')) { final dynamic notification = message['notification']; } } class Fcm { static final FirebaseRepository repository = FirebaseRepository(); static final FirebaseMessaging _fcm = FirebaseMessaging(); static initConfigure() { if (Platform.isIOS) _iosPermission(); _fcm.requestNotificationPermissions(); _fcm.autoInitEnabled(); _fcm.configure( onMessage: (Map<String, dynamic> message) async => NotificationManger.onMessage(message), onLaunch: (Map<String, dynamic> message) async => NotificationManger.onLaunch(message), onResume: (Map<String, dynamic> message) async => NotificationManger.onResume(message), onBackgroundMessage: myBackgroundMessageHandler, ); _fcm.getToken().then((String token) { print('token: $token'); repository.setUserNotifToken(token); }); } static _iosPermission() { _fcm.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true)); _fcm.onIosSettingsRegistered.listen((IosNotificationSettings settings) { print("Settings registered: $settings"); }); } }
and this is my NotificationManager.dart:
class NotificationManger { static BuildContext _context; static init({@required BuildContext context}) { _context = context; } static onMessage(Map<String, dynamic> message) { print(message); _showSnackbar(data: message); } static onLaunch(Map<String, dynamic> message) { print(message); } static onResume(Map<String, dynamic> message) { print(message); } static _showSnackbar({@required Map<String, dynamic> data}) { // showDialog(context: _context, builder: (_) => ); SnackBar snackBar = SnackBar( content: Text( data['data']['title'], style: TextStyle( fontFamily: 'Vazir', fontSize: 16.0, ), ), backgroundColor: ColorPalette.primary, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(45.0), ), elevation: 3.0, ); Scaffold.of(_context).showSnackBar(snackBar); } }
main.dart
class App extends StatelessWidget { final Store<AppState> store; App(this.store); @override Widget build(BuildContext context) { return StoreProvider( store: store, child: MaterialApp( ... ), ); } }
I am using redux and bloc, so any approach with these tools is ok for me.
This is my sample screen:
class Reminders extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: appBar, body: Center( child: Text('reminders'), ), ); } }
SOLUTION: Add
NotificationManger.init(globalKey: _scaffoldKey);
to all screens solve the problem.class Reminders extends StatelessWidget { final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); @override Widget build(BuildContext context) { NotificationManger.init(globalKey: _scaffoldKey); return Scaffold( key: _scaffoldKey, appBar: appBar, body: Center( child: Text('reminders'), ), ); } }
SOLUTION 2
UsingGet library to using only one function and no need to add it in all screen: https://pub.dev/packages/get