18

I am using Firebase cloud messaging for notifications, and i want to show a dialog or snackbar once i receive a notification when i am inside the application, my problem is that i am initializing the firebase configuration at the top of my widget tree (Splash screen once the app is starting)

_fireBaseMessaging.configure(
  onMessage: (Map<String, dynamic> message) async {
    dynamic data = message['data'];
    ................ // Adding a snackbar/alertdialog here doesn't work
  },
);

obviously if i set a dialog or snackbar it won't show since i need the context of my current page, is there any way to get the current context?

I also tried putting it inside the build widget of my splash screen but still the dialog isn't showing once i am on another page.

 @override
  Widget build(BuildContext context) {
    _fireBaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        dynamic data = message['data'];
        if (data['id'] == '1') {
          newPro = true;
        } else if (data['id'] == '2') {
          print("THIS WORKS!!!");
          showDialog(
              context: context,
              builder: (context) => AlertDialog(
                    content: ListTile(
                      title: Text("TEST"),
                      subtitle: Text("TEST"),
                    ),
                    actions: <Widget>[
                      FlatButton(
                        child: Text("OK"),
                        onPressed: () => Navigator.pop(context),
                      )
                    ],
                  ));
        }
      },
    );
Bahij.Mik
  • 1,358
  • 2
  • 9
  • 22

5 Answers5

13

I had the exact same issue, but I found a brilliant thread on GitHub. Basically, you can create a navigatorKey and pass that in to MaterialApp, and then use that navigatorKey to change route.

See how in this thread: https://github.com/brianegan/flutter_redux/issues/5#issuecomment-361215074

Tommy Østgaard
  • 159
  • 1
  • 4
9

I ended up using Overlay support:

https://pub.dev/packages/overlay_support

It is basically called at the very beginning of my tree just like wrapping providers at the main.dart, it worked like a charm, nothing else worked at all! Also here is a tutorial that helped me a lot:

https://medium.com/flutter-community/in-app-notifications-in-flutter-9c1e92ea10b3

Bahij.Mik
  • 1,358
  • 2
  • 9
  • 22
  • 1
    Using packages to accomplish little means is not a very good practice. As a rule of thumb, make sure you need more than half of the package's methods before installing it. – emanuel sanga Aug 05 '21 at 22:38
6

Because it makes me uncomfortable to have the answer embedded in a link, here is the answer (credit to xqwzts on Github).

Use a GlobalKey you can access from anywhere to navigate:

Create the key:

final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();

Pass it to your App:

new MaterialApp(
      title: 'MyApp',
      onGenerateRoute: generateRoute,
      navigatorKey: navigatorKey,
    );

Push routes:

navigatorKey.currentState.pushNamed('/someRoute');
tonymontana
  • 5,728
  • 4
  • 34
  • 53
Rob Caraway
  • 3,856
  • 3
  • 30
  • 37
5

An elegant solution to this problem is to use GlobalKey. That'll let you find the current BuildContext and do things with it.

You make a file called eg. global.dart looking like this:

import 'package:flutter/material.dart';

class GlobalVariable {
  static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}

You use this in your main() and MaterialApp() like this:

import 'global.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'fcm.dart';  // My Firebase Cloud Messaging code
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'screens/welcome_screen.dart';


void main() {
  print('Running main()');
  WidgetsFlutterBinding.ensureInitialized();
  Firebase.initializeApp();
  initializeFcm('', GlobalVariable.navState); // Sending the global key when initializing Firebase Cloud Messaging
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

  runApp(MyApp());
}


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WelcomeScreen(),
      navigatorKey: GlobalVariable.navState,  // Putting the global key in the MaterialApp
    );
  }
}

Then, in the file that handles Firebase Cloud Messaging, which I've named fcm.dart, you'll be able to use the GlobalKey to find the current context and use it, for example like this:

import 'package:blackbox/global.dart';
import 'online_screens/game_hub_screen.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

void initializeFcm(String token, GlobalKey myGlobalKey) async {
  print('Initializing Firebase Cloud Messaging...');
  await Firebase.initializeApp();

  FirebaseMessaging.onMessageOpenedApp.listen((remoteMsg) {
    // Using the currentContext found with GlobalKey:
    Navigator.push(GlobalVariable.navState.currentContext, MaterialPageRoute(builder: (context) {
      return GameHubScreen();
    }));
  });
}
Karolina Hagegård
  • 1,180
  • 5
  • 26
0

do the initializing inside a build method of your first widget in the tree ! which normally it called an App widget and it is StateLess StateFull widget and inside the build method you have access to the BuildContext

Amir Hossein Mirzaei
  • 2,325
  • 1
  • 9
  • 17