flutterfirebase-cloud-messagingflutter-beamer

Navigate from notification via beamer


I want to navigate to a specific page via beamer from a notification click.

In my main.dart I initialze my app and fcm. The class 'PushNotificationReceiver' should handle the notification logic.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await PushNotificationReceiver.instance.initialize();
  runApp(MultiProvider(providers: [
    // Some of my providers
  ], builder: (context, _) => MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();

    PushNotificationReceiver.instance.registerNotifications((route) => {
      context.beamToNamed(route)
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeProvider>(builder: (context, themeProvider, child) {
      return MaterialApp.router(
        routeInformationParser: BeamerParser(),
        routerDelegate: _beamerDelegate,
        backButtonDispatcher: BeamerBackButtonDispatcher(delegate: _beamerDelegate),
      );
    }
  }
}

I implemented the functions to receive and show local notifications but to simplify it I only paste the code for the click (removed null checks as well).

class PushNotificationReceiver {
  static PushNotificationReceiver _instance;
  void Function(String route) navigateFunction;

  static PushNotificationReceiver get instance {
    if (_instance == null) {
      _instance = new PushNotificationReceiver();
    }

    return _instance;
  }

  Future<void> initialize() async {
    await Firebase.initializeApp();
  }
  
  void registerNotifications(void Function(String route) navigateFunction) {
    this.navigateFunction = navigateFunction;

    // Called the other functions to receive notifications, but excluded them for simplicity.

    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      this.navigateFunction("/MyPage/${message.data["id"]}");
    });
  }
}

When I click on the notification I get the following error:

[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: 'package:beamer/src/beamer.dart': Failed assertion: line 40 pos 14: 'BeamerProvider.of(context) != null': There was no Router nor BeamerProvider in current context. If using MaterialApp.builder, wrap the MaterialApp.router in BeamerProvider to which you pass the same routerDelegate as to MaterialApp.router.

I tried it first without a function that I pass in and a GlobalKey in the main.dart with the same result. Any suggestions?


Solution

  • Found the solution. My first approach of a global key works if I wrap my MaterialApp.router in a Beamerprovider (like the error message suggested).

    final GlobalKey myGlobalKey = GlobalKey();
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await PushNotificationReceiver.instance.initialize();
      runApp(MultiProvider(providers: [
        // Some of my providers
      ], builder: (context, _) => MyApp()));
    }
    
    class MyApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return MyAppState();
      }
    }
    
    class MyAppState extends State<MyApp> {
      @override
      void initState() {
        super.initState();
    
        PushNotificationReceiver.instance.registerNotifications();
      }
      
      @override
      Widget build(BuildContext context) {
        return Consumer<ThemeProvider>(builder: (context, themeProvider, child) {
          return BeamerProvider(
            key: myGlobalKey,
            routerDelegate: _beamerDelegate,
            child: MaterialApp.router(
              routeInformationParser: BeamerParser(),
              routerDelegate: _beamerDelegate,
              backButtonDispatcher: BeamerBackButtonDispatcher(
                delegate: _beamerDelegate
              )
            )
          );
        }
      }
    }
    

    That leads to my push notification receiver:

    class PushNotificationReceiver {
      static PushNotificationReceiver _instance;
    
      static PushNotificationReceiver get instance {
        if (_instance == null) {
          _instance = new PushNotificationReceiver();
        }
    
        return _instance;
      }
    
      Future<void> initialize() async {
        await Firebase.initializeApp();
      }
      
      void registerNotifications(void Function() {
        // Called the other functions to receive notifications, but excluded them for simplicity.
    
        FirebaseMessaging.onMessageOpenedApp.listen((message) {
          myGlobalKey.currentContext.beamToNamed("/MyPage/${message.data["id"]}");
        });
      }
    }
    

    I hope this will help some others too.