如何在Flutter中使用GoRouter进行路由跳转而不需要上下文?

9
我想解决的问题: 我的应用程序使用GoRouter需要能够从main()路由到具有名称的路由。由于大多数路由都是'context.go'格式,因此我无法在main中执行。 背景: 我的应用程序使用GoRouter。 GetX让我定义命名路由并从main()传递参数的便捷性非常完美。 但是,GetX和GoRouter最终会给我带来问题。 GoRouter最终在应用程序的其他部分没有上下文。 如果有一种简单共存的方法,我会持开放态度。 我使用GetIt包的服务定位器模式与navigatorKey相关联。当我测试时它可以工作--但这涉及创建两个MaterialApps。 然而,此应用程序使用似乎不使用navigatorKey的GoRouter。 我想从main()进入特定路线。与Navigator 2.0 for MaterialApp一样,服务定位器模式似乎可以像GoRouter一样工作--但我找不到如何执行的示例。 更详细的上下文信息: 以下是我当前在main()中所拥有的。 您可以看到我面临的关键挑战是数据参数的侦听器位于main中(我从第三方SDK获取了此内容--我不需要它在main中,但它需要在应用程序状态无关的情况下进行侦听)。
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;
  });
}

以下是locator.service.dart的外观:

final locator = GetIt.instance;

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

当我能够将navigatorKey附加并从侦听器内导航时,上述内容是有效的。但由于应用程序的其余部分使用GoRouter,这似乎不起作用。


只需将默认路由设置为着陆页面,然后从那里决定您真正想要去哪里。 - Randal Schwartz
@RandalSchwartz -- 仅当用户点击深度链接时,着陆页面才应路由到该页面。如果我将其设置为初始路由,着陆页面是否会在不同情况下打开? - satchel
4个回答

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

你可以通过以下方式在 NavigationService 中获取上下文并使用它:
NavigationService.ctx?.go(...)

你可能会遇到的问题是,直到您的第一个页面开始构建之前,应用状态上的ctx将为null。如果侦听器在ctx仍为null时具有数据,则路由将无法工作。但是,您可以像这样处理这种情况:
在主函数或服务中定义一个全局的tempPageToGo,并且...
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(...);

10
我们可以使用路由器对象在没有上下文的情况下进行导航。
final router= GoRouter(....);

现在进行导航,请使用以下命令:

router.push(...);

3
我正在研究如何在我的项目中采用go_router,并且我也卡住了这个使用案例(在我的情况下,我尝试证明我可以从appsflyer SDK的回调中导航到延迟链接)。
解决方案是,像go_router这样的路由器可以让我们从声明路由器以下的上下文或重定向状态中导航。因此,我们可以将所有影响导航的状态都包装起来。 这就是我从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;
},

在你的情况下,你可以在appRouterStateProvider或其他地方实现setOnReferralDataReceivedListener。并将其用作GoRouter构造函数中的refreshListenable参数。 希望这可以帮助你。

3
很不幸,如果我是你,我会放弃使用GetX或GoRouter之一。
实际上,我只会放弃GetX。
原因是GetX在幕后执行魔法,让开发人员摆脱了BuildContext的责任和使用,但这显然是一种反模式,因为Flutter内置导航明显使用context:例如。
GoRouter是基于context构建的,并简化了执行“Navigator 2.0”操作所需的许多实现。
如果您尝试实现深度链接,则应该在根小部件中将看起来像这样:
return MaterialApp.router(  // Flutter's Router 2.0 usage
  title: 'MyApp',
  routeInformationProvider: myGoRouter.routeInformationProvider,
  routeInformationParser: myGoRouter.routeInformationParser,
  routerDelegate: myGoRouter.routerDelegate,
);

如果GetX让你可以在那里放置myGoRouter,那么你应该可以愉快地使用它。但是像我之前说的那样,每次需要明确导航时,你都需要上下文。

我尝试着实现这个,并在MaterialApp.router下面拥有相似的安装。但是我如何从主路由到达一个路由呢? - satchel
我遇到的主要问题是能够基于监听器中的事件进行路由。似乎将这些事件放入流中是前进的道路,但我不清楚如何做到这一点,因为第三方深度链接SDK参考实现将监听器放在main()中。所以我希望从监听器内部路由。这在GetX中可以工作。但是使用GetX和GoRouter会导致问题破裂。 - satchel
@satchel,您需要阅读GoRouter的深度链接文档才能使用深度链接。https://gorouter.dev/declarative-routing#deep-linking 从“main”开始,您实际上很少需要命令式地引导用户。关于第二个评论:您可能需要重定向逻辑(https://gorouter.dev/redirection#redirection)和/或正确声明GoRouter路由(https://gorouter.dev/sub-routes#sub-routes)。 - venir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接