How to navigate without context in flutter app?

Flutter

Flutter Problem Overview


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

Flutter Solutions


Solution 1 - Flutter

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');

Solution 2 - Flutter

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.

Solution 3 - Flutter

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

Solution 4 - Flutter

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.

Solution 5 - Flutter

You can actually pass the context outside the builder(), by adding parameter value to the method.

for example, if you need to use navigator push in dart file without builder.

you can do this

methodName(String name, String address, context){
Navigator.pushNamed(context, '/login');
}

Solution 6 - Flutter

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');

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAshutosh SharmaView Question on Stackoverflow
Solution 1 - FluttertsdevelopmentView Answer on Stackoverflow
Solution 2 - Fluttery.selimdoganView Answer on Stackoverflow
Solution 3 - FlutterjdmeView Answer on Stackoverflow
Solution 4 - FlutterYusuf AmrView Answer on Stackoverflow
Solution 5 - FlutterSoftFieTeaView Answer on Stackoverflow
Solution 6 - Fluttera0x2View Answer on Stackoverflow