Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

show snackbar on fcm notification on every screen

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

like image 499
BeHappy Avatar asked Apr 02 '20 19:04

BeHappy


People also ask

How do I display snackbar in flutter?

Building, displaying, and customizing the SnackBarLaunch Android Studio or another IDE of your choice. Start a new Flutter project. Select Flutter Application and name the project something like “snackbardemo”

How do I send a picture in FCM notification?

Stay organized with collections Save and categorize content based on your preferences. The FCM HTTP v1 API and the Notifications composer support sending image links in the payload of a display notification, for image download to the device after delivery.

How do I use snackbar without context flutter?

then you can simply call the Get. snackbar() to show the snackbar where you want it to be displayed. You can use this package when you don't have the context too. Save this answer.


1 Answers

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.

like image 164
Jwildsmith Avatar answered Oct 20 '22 04:10

Jwildsmith