Flutter: Notification Navigation from Background Task without context issue

695

I solved it by reacting to two different events.

If the App starts, i check if the app was launched by a notification. This can be done in createState or initState in main.dart. This code is useful for that.

final NotificationAppLaunchDetails? notificationAppLaunchDetails =
  await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
String initialRoute = HomePage.routeName;
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
  selectedNotificationPayload = notificationAppLaunchDetails!.payload;
  initialRoute = SecondPage.routeName;
}

If the app is in background and a notification launches the app again, you must use Widgets Binding Observer and react to the App Resume event. There is an article at Medium which has example code for this case. Have a look at it here.

There is one drawback when reacting to App Resume Event and using the Flutter Local notifications Plugin. The aforementioned code always delivers true once triggered by an notification, even if the app entered background state again and was resumed manually by an user. This means code for changing a page will always be called, even if you did not click an notification. Therefore, I´m using a boolean variable to trigger the App State Resume code once. Obviously, if you enter the app via notification ,the app gets resumed and you get a second notification, the code for changing a page will not be executed. It´s a workaround, but for my case, it´s good enough.

Share:
695
Nyancat92
Author by

Nyancat92

Updated on December 01, 2022

Comments

  • Nyancat92
    Nyancat92 over 1 year

    My App does the following: It runs a background Task using Flutter Workmanager which checks some values and then it throws a Notification via Flutter Local Notification. In the initialize method from FlutterLocalNotifications Plugin, i can specify a inline fuction, which should navigate to a page. Since i dont have a Builder context, i must use a Navigator Key with OnGenerateRoute to forward the user to a site. However, this doesn`t work and i don´t know why. I know that this code is useful when the app gotkilled.

    Example Code

    final NotificationAppLaunchDetails? notificationAppLaunchDetails =
          await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
      String initialRoute = HomePage.routeName;
      if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
        selectedNotificationPayload = notificationAppLaunchDetails!.payload;
        initialRoute = SecondPage.routeName;
      }
    

    But what to do when the app is still alive? My Project code is listed below.

    Main.Dart

    void main() {
    
      WidgetsFlutterBinding.ensureInitialized();
      Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
      Workmanager().registerPeriodicTask("1", "test",frequency: Duration(minutes: 15));
      runApp(MyApp());
    }
    
    class MyApp extends StatefulWidget {
      @override
      State createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      // This widget is the root of your application.
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          initialRoute: "/",
          navigatorKey: NavigationService.navigatorKey,
          onGenerateRoute: RouteGenerator.generateRoute
        );
      }
    }
    

    RouteGenerator.dart

    class RouteGenerator {
      static Route<dynamic> generateRoute(RouteSettings settings) {
        final args = settings.arguments;
    
        switch(settings.name) {
          case '/first':
            return MaterialPageRoute(builder: (_) => Page1(title: "First"));
          case '/second':
            return MaterialPageRoute(builder: (_) => Page2(title: "Second"));
          case '/third':
            return MaterialPageRoute(builder: (_) => Page3(title: "Third"));
          case '/fourth':
            return MaterialPageRoute(builder: (_) => Page4(title: "Fourth"));
        }
        return MaterialPageRoute(builder: (_) => Page0(title: "Root!"));
      }
    }
    
    class NavigationService {
      static final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
    
      static Future<dynamic> navigateTo(String routeName) {
        return navigatorKey.currentState!.pushNamed(routeName);
      }
    }
    

    service.dart

    
    class DevHttpOverrides extends HttpOverrides {
      @override
      HttpClient createHttpClient(SecurityContext? context) {
        return super.createHttpClient(context)
          ..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
      }
    }
    
    void callbackDispatcher() {
      Workmanager().executeTask((task, inputData) async{
            HttpOverrides.global = new DevHttpOverrides();
            var url = 'https://172.16.0.100/handler.php?page=settings';
            http.Response response = await http.get(Uri.parse(url));
            List<dynamic> list = jsonDecode(response.body);
    
    
            SharedPreferences prefs = await SharedPreferences.getInstance();
            var usage = "Beides";
            var checkValue = "temp_out";
            var borderValueString = "14.9";
            var checktype = "Grenzwert überschreiten";
    
            var borderValueDouble;
            var message = "";
            if(usage != "Nur Home Widgets" && checkValue != "" && borderValueString != "" && checktype != "")
            {
              var value = list[0][checkValue];
    
              if (double.tryParse(borderValueString) != null && double.tryParse(value) != null)
              {
                borderValueDouble = double.parse(borderValueString);
                value = double.parse(value);
              }
    
              if (checktype == "Grenzwert unterschreiten")
              {
                if (borderValueDouble is double)
                {
                  if (value <= borderValueDouble)
                  {
                    message = "Grenzwert unterschritten";
                  }
                }
              }
              else if (checktype == "Grenzwert überschreiten")
              {
                if (borderValueDouble is double)
                {
                  if (value >= borderValueDouble)
                  {
                    message = "Grenzwert überschritten";
                  }
                }
              }
              else if (checktype == "Entspricht Grenzwert")
              {
                if (borderValueDouble == value)
                {
                  message = "Grenzwert erreicht";
                }
              }
            }
    
            if(message != "")
            {
              FlutterLocalNotificationsPlugin flip = new FlutterLocalNotificationsPlugin();
              var android = new AndroidInitializationSettings('@mipmap/ic_launcher');
              var ios = new IOSInitializationSettings();
    
              var settings = new InitializationSettings(android: android, iOS: ios);
              flip.initialize(settings, onSelectNotification: (String? payload) async {
                await NavigationService.navigatorKey.currentState!.push(MaterialPageRoute(builder: (context) => Page4(title: "Hello")));
          
      });
                
    
    
              var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
                  '1',
                  'weatherstation',
                  'Notify when values change',
                  importance: Importance.max,
                  priority: Priority.high
              );
    
              var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
    
              var platformChannelSpecifics = new NotificationDetails(
                  android: androidPlatformChannelSpecifics,
                  iOS: iOSPlatformChannelSpecifics);
    
              await flip.show(0, message,
                  'App öffnen für weitere Details',
                  platformChannelSpecifics, payload: 'Default_Sound'
              );
            }
    
        return Future.value(true);
      });
    }