tomgilder / routemaster

Easy-to-use Navigator 2.0 router for web, mobile and desktop. URL-based routing, simple navigation of tabs and nested routes.
https://pub.dev/packages/routemaster
MIT License
328 stars 61 forks source link

How to dynamically choose the initial route? #217

Open agordeev opened 2 years ago

agordeev commented 2 years ago

There are several cases in my app:

How to achieve this with this great library? I tried to use PlatformRouteInformationProvider, but Routemaster crashes on this:

_navigate(
      uri: PathParser.getAbsolutePath(
        basePath: currentConfiguration!.fullPath,
        path: path,
        queryParameters: queryParameters,
      ),
      queryParameters: queryParameters,
      isReplacement: false,
      navigationResult: result,
    );

complaining that currentConfiguration is null.

tomgilder commented 2 years ago

Hi, can you give me an example of your route map please?

agordeev commented 2 years ago

Thanks for trying to help!

  @override
  Widget build(BuildContext context) {
    precacheImages(context);
    late RoutemasterDelegate routemaster;
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => AppCubit(
            authRepository: AuthRepository(),
          ),
        ),
        BlocProvider(
          create: (context) => SignUpCubit(
            appCubit: BlocProvider.of<AppCubit>(context),
            authRepository: AuthRepository(),
          ),
        ),
      ],
      child: BlocConsumer<AppCubit, AppState>(
        // TODO: Find a better way to set the initial route.
        // This listener can be called multiple times, which we don't want.
        listener: (context, state) => state.maybeWhen(
          unverifiedEmail: (email) => routemaster.push('/verify_email'),
          incompleteUser: () => routemaster.push('/add_name'),
          orElse: ignore,
        ),
        builder: (context, state) {
         // States are:
         // authenticated
         // unverifiedEmail
         // incompleteUser
         // unauthenticated

          routemaster = RoutemasterDelegate(
            routesBuilder: (ctx) => state.maybeMap(
              authenticated: (state) => _buildRouteMap(state),
              orElse: () => buildUnauthenticatedRouteMap(),
            ),
          );
          return MaterialApp.router(
            theme: buildAppTheme(context),
            routeInformationParser: const RoutemasterParser(),
            routerDelegate: routemaster,
          );
        },
      ),
    );
  }
michaelbushe commented 2 years ago

Here's how you can do it more plainly and surely.

class MainApp extends StatelessWidget {
  final AppCubit appCubit;
  const MainApp(this.appCubit, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var appRouteInformationProvider = PlatformRouteInformationProvider(
        initialRouteInformation:
            RouteInformation(location: appCubit.initialRoute));
    return BlocProvider<AppCubit>.value(
        value: appCubit,
        child: MaterialApp.router(
          debugShowCheckedModeBanner: false,
          title: 'Delorean',
          theme: themeData,
          routerDelegate: AppRouter(appCubit.isLoggedIn).routerDelegate,
          routeInformationProvider: appRouteInformationProvider,
          routeInformationParser: const RoutemasterParser(),
        ));
  }
}
michaelbushe commented 2 years ago

Oh! But I still sometimes get the same bug as you. Mine happens when a user logs in and the OAuth service redirects back to the web app url that loads a specific route.
It's not the best code - the field should be declared non-null or null should be dealt with without it rhowing Null. I'm going to try to fork and patch it for now to see if it helps.