csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
441 stars 97 forks source link

Pb when starting application on a specific page using deep link #155

Closed fvisticot closed 2 years ago

fvisticot commented 3 years ago

Application running on iOS DeepLinking works if deep link destination is the "Home page" of the app

When deep link is defined to go on a specific route : "/events/12/gallery" Deep link is correctly received on the "onLink" listener:

What could be the issue ?

FirebaseDynamicLinks.instance.onLink(
        onSuccess: (PendingDynamicLinkData? dynamicLink) async {
      final Uri? deepLink = dynamicLink?.link;

      if (deepLink != null) {
        print("DeepLink: ${deepLink.path}"); <----- deepLink.path is correct ("/events/12/gallery")
        context.go(deepLink.path);
      }
    }, onError: (OnLinkErrorException e) async {
      print('onLinkError');
      print(e.message);
    });

But the router is not fired and navigation stays on the home page...

GoRoute(
              path: '/events/:id/gallery', <---- This route should be called on the deeplink
              pageBuilder: (context, state) {
                return MaterialPage<void>(
                  key: state.pageKey,
                  child: MediasGalleryPage(eventId: state.params['id']!),
                );
              }),
csells commented 3 years ago

What's the debug output when you set the debugDiagnosicLog flag to true on the GoRouter constructor?

fvisticot commented 3 years ago

Nothing is displayed on the routeur logs. And context is not null when calling the go method

csells commented 3 years ago

If nothing is shown in the logs from GoRouter after setting that flag then you don't have an instant of GoRouter to route to.

fvisticot commented 3 years ago

So you mean that le deep link is fired and the router is still not "available" even if I have already used / navigated into the app with the router before the deepLink was fired ?

When the Link is fired, If I print the goRouter the variable is not null

any advice / sample code to help ?

csells commented 3 years ago

If you start the GoRouter with the debug flag set, it will produce debug output. Can you share that?

fvisticot commented 3 years ago
Syncing files to device iPhone fred...
flutter: GoRouter: known full paths for routes:
flutter: GoRouter:   => /
flutter: GoRouter:   => /events/:id
flutter: GoRouter:   => /events/:id/gallery
flutter: GoRouter:   => /trials/:id/gallery
flutter: GoRouter:   => /trials/:id
flutter: GoRouter:   => /trials/:id/competitors
flutter: GoRouter:   => /trials/:id/competitors/:bib
flutter: GoRouter: setting initial location /

and when I click on the deepLink and redirect to the app:

flutter: DeepLink: /events/b27ce425-3fd2-11ec-a052-fa163e9890e4/gallery
flutter: Context: MyApp(state: _MyAppState#be8ae)
flutter: Router: Instance of 'GoRouter'

the main code (without imports)

void main() async {
  Bloc.observer = AppStateObserver();
  GoRouter.setUrlPathStrategy(UrlPathStrategy.path);

  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    FlutterError.onError = (FlutterErrorDetails details) {
      FlutterError.dumpErrorToConsole(details);
    };
    runApp(MyApp());
  }, (Object error, StackTrace stack) {
    print(stack);
  });
}

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final FirebaseAnalytics analytics = FirebaseAnalytics();

  final _goRouter = GoRouter(
      routes: [
        GoRoute(
          path: '/',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: HomePage(),
          ),
        ),
        GoRoute(
          path: '/events/:id',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: EventPage(state.params['id']),
          ),
        ),
        GoRoute(
            path: '/events/:id/gallery',
            pageBuilder: (context, state) {
              return MaterialPage<void>(
                key: state.pageKey,
                child: MediasGalleryPage(eventId: state.params['id']!),
              );
            }),
        GoRoute(
          path: '/trials/:id/gallery',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: MediasGalleryPage(trialsIds: [state.params['id']!]),
          ),
        ),
        GoRoute(
          path: '/trials/:id',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: TrialPage(state.params['id']),
          ),
        ),
        GoRoute(
          path: '/trials/:id/competitors',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: CompetitorsPage(state.params['id']!),
          ),
        ),
        GoRoute(
          path: '/trials/:id/competitors/:bib',
          pageBuilder: (context, state) => MaterialPage<void>(
            key: state.pageKey,
            child: CompetitorRankingPage(
                state.params['id']!, state.params['bib']!),
          ),
        ),
      ],
      errorPageBuilder: (BuildContext context, GoRouterState state) {
        return MaterialPage(child: NotFoundPage());
      },
      debugLogDiagnostics: true,
      initialLocation: '/');

  @override
  void initState() {
    super.initState();
    _initDynamicLinks();
  }

  void _initDynamicLinks() async {
    FirebaseDynamicLinks.instance.onLink(
        onSuccess: (PendingDynamicLinkData? dynamicLink) async {
      final Uri? deepLink = dynamicLink?.link;

      if (deepLink != null) {
        print("DeepLink: ${deepLink.path}");
        print("Context: $context");
        print("Router: $_goRouter");

        context.go(deepLink.path);
        //context.go('/trials/d1e79988-3fd2-11ec-a052-fa163e9890e4/competitors');
      }
    }, onError: (OnLinkErrorException e) async {
      print('onLinkError');
      print(e.message);
    });

    final PendingDynamicLinkData? data =
        await FirebaseDynamicLinks.instance.getInitialLink();
    final Uri? deepLink = data?.link;

    if (deepLink != null) {
      context.go(deepLink.path);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
        providers: [
          BlocProvider<MediasBloc>(
              create: (context) => MediasBloc(TibibLiveApi())),
        ],
        child: Layout(
          child: MaterialApp.router(
            debugShowCheckedModeBanner: false,
            routeInformationParser: _goRouter.routeInformationParser,
            routerDelegate: _goRouter.routerDelegate,
            title: 'Tibib live',
            theme: ThemeData(
              textTheme: GoogleFonts.latoTextTheme(
                Theme.of(context).textTheme,
              ),
              brightness: Brightness.light,
              primaryColor: Colors.white,
              colorScheme: ColorScheme.light(
                primary: Colors.white,
                primaryVariant: Colors.red,
                onPrimary: Color.fromRGBO(0, 152, 187, 1),
                secondary: Color.fromRGBO(0, 152, 187, 1),
                //onSecondary: Colors.pink,
              ),
            ),
            localizationsDelegates: [
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
            ],
            supportedLocales: [
              const Locale('en', ''), // English, no country code
              const Locale('fr', ''), // Spanish, no country code
            ],
          ),
        ));
  }
}

class AppStateObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    //print('${bloc.runtimeType} $change');
  }
}
csells commented 2 years ago

what happens when you call context.go(deepLink.path)? is there any debug output? what if you call _goRouter.go(deepLink.path) instead?

DeD1rk commented 2 years ago

The context used in _initDynamicLinks is that of the _MyAppState. That context is higher in the widget tree than your MaterialApp, and hence the GoRouter. So indeed calling from _goRouter directly should be better.

fvisticot commented 2 years ago

The context used in _initDynamicLinks is that of the _MyAppState. That context is higher in the widget tree than your MaterialApp, and hence the GoRouter. So indeed calling from _goRouter directly should be better.

Tx a lot it was the pb. using _goRouter.go fix is OK !!

fvisticot commented 2 years ago

what happens when you call context.go(deepLink.path)? is there any debug output? what if you call _goRouter.go(deepLink.path) instead?

It works great with _goRouter.go(deepLink.path) !!

Tx a lot for the great support !

fvisticot commented 2 years ago

It works great with _goRouter.go(deepLink.path) !!

csells commented 2 years ago

@fvisticot was there an exception in your debug output when you attempted to use context.go()? there should've been something that told you that there was no GoRouter found in the current context.