简体   繁体   中英

how can I route using GoRouter in Flutter without context?

The problem I want to solve: My app which uses GoRouter needs to be able to route to a named route from within main(). Since most routing is of the form 'context.go' I cannot do so within main.

Background

My app uses GoRouter. The ease with which GetX had let me define named routes and pass parameters from main() was perfect.

However, GetX and GoRouter eventually causes problems for me. GoRouter would eventually have no context in other parts of the app.

If there were a way to have them co-exist simply, I'd be open to it.

I had used the service locator pattern with the GetIt package to associate with a navigatorKey. It would work when I tested it -- but this involved creating two MaterialApps.

However, this app uses GoRouter which doesn't seem to use the navigatorKey.

I would like to go to a specific route from within main (). It seems like the service locator pattern could work for GoRouter as it did with Navigator 2.0 for MaterialApp -- but I can't find an example of how to do so.

More detailed context:

Here is what I have currently in main().

You can see the key challenge I have is that the listener for the data parameters being passed in lives in main (I got this from the third-party SDK -- I don't need it to be in main but it needs to listen regardless of the state of the app).

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FFAppState(); // Initialize FFAppState

  GetSocial.addOnInitializedListener(() => {
        // GetSocial SDK is ready to use
      });

  setupLocator();

  runApp(MyApp());

  locator<LandingPageData>().referralID = "defaultReferralID";

  registerListeners();
}

void registerListeners() {
  Invites.setOnReferralDataReceivedListener((received) {
    globalReferralData = received;
    print(globalReferralData);
    print(globalReferralData.linkParams);

    print("listener - socialdata");

    String passedReferralID =
        globalReferralData.linkParams['referralID'].toString();
    String passedCreatorID =
        globalReferralData.linkParams['creatorID'].toString();
    String passedCampaignID =
        globalReferralData.linkParams['\$campaign_id'].toString();

    print(passedReferralID);
    print(passedCreatorID);
    print(passedCampaignID);

    // How can I route to a named Route?

    locator<LandingPageData>().referralID = passedReferralID;
    locator<LandingPageData>().creatorID = passedCreatorID;
    locator<LandingPageData>().campaignID = passedCampaignID;
  });
}

Here is what the locator.service.dart looks like:

final locator = GetIt.instance;

class NavigationService {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
  // final GlobalKey<ScaffoldMessengerState> navigatorKey = GlobalKey<ScaffoldMessengerState>();
}

The above worked when I could attach to a navigatorKey and then navigate from within the listener. But that doesn't seem to work since the rest of the application uses GoRouter.

static BuildContext? get ctx => myGoRouter.routerDelegate.navigatorKey.currentContext;

you can get context in your NavigationService in this way and use it like

NavigationService.ctx?.go(...)

the problem you may face is that ctx will be null on app state till your first page starts to be built. In the case your listener has a data while ctx is still null, routing won't work. but you can handle this situation like:

define a global tempPageToGo in main func or a service and

var _ctx = NavigationService.ctx;
if(_ctx == null) {
  tempPageToGo = anyPageDataYouWant;
  while((await Future.delayed(Duration(seconds: 1))) == null) {
    if(_ctx != null) {
      _ctx!.go(...);
      break;
    }
  }
} else _ctx!.go(...);

Unluckily, if I were you, I'd either drop the usage of GetX or of GoRouter.

Actually, I'd just drop GetX.

The reason is that GetX performs magic under the hood that lifts the developer the responsibility and usage of BuildContext , but that's clearly an anti-pattern, as the built-in navigation from Flutter clearly uses context : think of Navigator.of , for example.

GoRouter is built around context , and simplifies a lot of the implementations needed to perform "Navigator 2.0" actions.

If you're trying to implement deep linking, your MaterialApp should look like this in your root widget:

return MaterialApp.router(  // Flutter's Router 2.0 usage
  title: 'MyApp',
  routeInformationProvider: myGoRouter.routeInformationProvider,
  routeInformationParser: myGoRouter.routeInformationParser,
  routerDelegate: myGoRouter.routerDelegate,
);

If GetX enables you to put myGoRouter there, then you should be good to go. But as I said before, everytime you need explicit navigation, you need context.

I'm in researching to adopt go_router in my project, and i was also stuckled for this usecase ( in my case i tried to prove that i can navigate from deferred link that callback from appsflyer SDK ).

For solution, like that go_router allows us to either navigate from context that is below the router declaration or from the redirect state. So we can wrap up all the state that effect the navigation on that. This is how i redirect from appRouterState

redirect: (GoRouterState state) {
  String? redirection(GoRouterState state) {
    final appRouterState = ref.read(appRouterStateNotifierProvider);
    final isAuthed = appRouterState.email != null;

    if (appRouterState.deferredLink != state.location && appRouterState.deferredLink != null) {
        return appRouterState.deferredLink;
    }

    if (state.location != '/login' && !isAuthed) return '/login';
    if (state.location == '/login' && isAuthed) return '/';

    return null;
  }
  final result = redirection(state);
  return result;
},

In your case, you may implement setOnReferralDataReceivedListener in the appRouterStateProvider or something. And use it for refreshListenable param in the GoRouter constructor.

Hope this helps.

I found it not easy to use go_router without context,but it easy in routemaster

read more:https://pub.dev/packages/routemaster#navigate-without-a-context

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM