How to navigate without context in flutter app?

Viewed 46540

I have an app that recieves push notification using OneSignal. I have made a notification opened handler that should open specific screen on click of the notification. How can i navigate to a screen without context. or how can I open specific screen on app startup. My code:

OneSignal.shared.setNotificationOpenedHandler((notification) {
  var notify = notification.notification.payload.additionalData;
  if (notify["type"] == "message") {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => DM(user: notify['id']),
      ),
    );
  }
  if (notify["type"] == "user") {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => Profileo(notify["id"]),
      ),
    );
  }
  if (notify["type"] == "post") {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => ViewPost(notify["id"]),
      ),
    );
  }
});

I am able to achieve this when the app is opened for the first time but It only opens the homepage If i close the app and even if I re-open it. I guess that is because the context is changed.

Please Help!!

6 Answers

Look at this here: https://github.com/brianegan/flutter_redux/issues/5#issuecomment-361215074

You can set a global key for your navigation:

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

Pass it to MaterialApp:

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

Push routes:

navigatorKey.currentState.pushNamed('/someRoute');

You can use this wonderful plugin: https://pub.dev/packages/get

Description from the package: A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars from anywhere in your code without context.

Get.to(NextScreen()); // look at this simplicity :)
Get.back(); //  pop()
Get.off(NextScreen()); // clears the previous routes and opens a new screen.

This solution is general if you want to navigate or to show dialog without context using globalKey especially with Bloc or when your logic is separated from your UI part.

Firstly install this package:

Not: I'm using null safety version

  get_it: ^7.2.0

Then create a separate file for your service locator:

service_location.dart

    import 'package:get_it/get_it.dart';
    
    GetIt locator = GetIt.instance;
    
    class NavigationService {
      final GlobalKey<NavigatorState> navigatorKey =
          new GlobalKey<NavigatorState>();
      Future<dynamic> navigateTo(String routeName) {
        return navigatorKey.currentState!.pushNamed(routeName);
      }
    
      void setupLocator() {
        locator.registerLazySingleton(() => NavigationService());
      }

  void showMyDialog() {
    showDialog(
        context: navigatorKey.currentContext!,
        builder: (context) => Center(
              child: Material(
                color: Colors.transparent,
                child: Text('Hello'),
              ),
            ));
  }
    }

on main.dart:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  NavigationService().setupLocator();
  runApp(MyApp());
}
// add navigatorKey for MaterialApp

 MaterialApp(
        navigatorKey: locator<NavigationService>().navigatorKey,
      ),

at your business logic file bloc.dart define this inside the bloc class or at whatever class you want to use navigation inside Then start to navigate inside any function inside.

class Cubit extends Cubit<CubitState> {
  final NavigationService _navigationService = locator<NavigationService>();
  void sampleFunction(){
       _navigationService.navigateTo('/home_screen'); // to navigate
       _navigationService.showMyDialog(); // to show dialog

    }
}

Not: I'm using generateRoute for routing.

Quickest fix is above using global navigatorKey (like @tsdevelopment answered). To fix undefined navigatorKey, it must be imported from where it is instantiated (for this example in main.dart).

Your main.dart

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

void main() {
  runApp(CupertinoApp(
    title: 'Navigate without context',
    initialRoute: '/',
    navigatorKey: navigatorKey, // important
    onGenerateRoute: ...
  ));
}

For example you are in your lib/utils/api.dart

import 'package:your_package_name/main.dart'; // important

abstract class API {
  static Future<dynamic> get() async {
     // call some api
     ...
     // then you want to navigate to specific screen like login
     navigatorKey.currentState?.pushNamed('/login'); // navigate to login, with null-aware check
  }
}

Also have a gist example if you prefer in a service approach. Check this: https://gist.github.com/josephdicdican/81e59fad70530eac251ad6c28e2dcd4b

You can use this no_context_navigation plugin

as the name suggests, we can navigate without context

navService.pushNamed('/detail_screen', args: 'From Home Screen');

I know this is an old post, but there is a package that handles navigation without the build context (Using a navigator key) called flutter_navigator: https://pub.dev/packages/flutter_navigator

It allows you to navigate something like this:

_flutterNavigation.push(//YourRoute);

Everything seems to be mapped 1:1 with Flutter's Navigator API, so there is no worries there!

Related