83

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!!

Ashutosh Sharma
  • 1,419
  • 2
  • 12
  • 22

8 Answers8

169

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');
BIS Tech
  • 17,000
  • 12
  • 99
  • 148
tsdevelopment
  • 1,785
  • 1
  • 7
  • 7
  • 15
    navigatorKey is undefined – temirbek Mar 21 '19 at 11:45
  • 5
    @temirbek define navigatorKey in your main.dart and import it in your other file. – Mustafa Nov 25 '19 at 17:19
  • 3
    in addition, set the *navigatorKey* property of the MaterialApp to the declared global key (navigatorKey), not the *key* property of MaterialApp – tewshi Dec 01 '19 at 12:53
  • 2
    @Mustafa can you please provide sample? I have read many blogs and forums all are giving same solutions but navigator key is accessible from my dio interceptor. – Jenish Apr 12 '21 at 11:47
  • how can we pass multiple parameters? kindly suggestion. Thanks. – Kamlesh Jul 27 '21 at 10:40
  • @Kamlesh create a custom class, and pass that class as an argument – sarah Aug 04 '21 at 08:00
  • 2
    We are using go router to define the names routes. Will this still work? – Satchel Jul 12 '22 at 20:12
  • How to pass variables at the time of pushing to new routes? passing data to the class parameter with context seems different from this. – DmDev Aug 24 '22 at 12:18
  • Thanks for this, I have been stuck for some time now trying to figure a way around managing navigation in an environment that employs both Nav 2.0 and Nav 1.0. This approach helped me figure a solution to my problem. Thanks a million time. I wish there was a super upvote to give it to you. – rashidotm Sep 14 '22 at 13:23
  • Is there any drawback to use this approach more frequently? – Nijat Namazzade Oct 06 '22 at 08:31
  • It's great, but if you want to restart the app by resetting the key (https://stackoverflow.com/a/50116077/9885611), this method will not work. – Hossein Yousefpour Mar 13 '23 at 09:43
  • Similar to the question by @Satchel: Does this method interoperate with `Navigator.of(context).pop()` etc.? Or does this create a second `NavigatorState`? Does this then break libraries that directly call `Navigator.of(context)`? – Luke Hutchison Mar 15 '23 at 05:22
28

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.
y.selimdogan
  • 599
  • 5
  • 11
7

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

josevoid
  • 577
  • 7
  • 12
5

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.

Yusuf Amr
  • 487
  • 6
  • 5
0

You can use this no_context_navigation package

as the name suggests, we can navigate without context

navService.pushNamed('/detail_screen', args: 'From Home Screen');
a0x2
  • 1,995
  • 1
  • 18
  • 26
0

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!

Luke Moody
  • 41
  • 2
  • 6
0

The accepted answer works fine. I just wanted to add if anyone wants to do pushReplacement without context. In that case this can help

navigatorKey.getCurrentState()?.pushNamed(
        '/someRoute',
    ).then((value) {
      if (value != null) {
        navigatorKey.getCurrentState()?.pushReplacement(value as Route<Object>);
      }
    });
Fahmida
  • 1,050
  • 8
  • 19
0

You can use deep_route navigation package. This will help us navigate any page from anywhere within the code even from the business logic. It doesn't require any context.

Replace MaterialApp with

void main() => runApp(DeepMaterialApp(home: Home()));

Navigate using

DeepRoute.toNamed('/RouteName', arguments: 'sample');
DeepRoute.to(NextPage());
deepak raj
  • 3,331
  • 1
  • 12
  • 20