Flutter Interview Handbook

Deep Linking & Declarative Routing

100%

Deep Linking & Declarative Routing

Modern Flutter navigation requires declarative URL routing (go_router built on Navigator 2.0) and seamless Deep Linking via iOS Universal Links and Android App Links.


 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                              DEEP LINKING STANDARDS                                    β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ 1. Custom URL Schemes         β”‚ 2. Universal Links (iOS) & App Links (Android)        β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ - Scheme: `myapp://product/1` β”‚ - Domain: `https://example.com/product/1`              β”‚
 β”‚ - No ownership verification   β”‚ - Requires domain ownership verification               β”‚
 β”‚ - Vulnerable to scheme hijackingβ”‚ - `apple-app-site-association` & `assetlinks.json`   β”‚
 β”‚ - Shows fallback popups       β”‚ - Opens app directly without browser prompt             β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Domain Verification Protocol

To verify domain ownership:

  • iOS Universal Links: Host apple-app-site-association at https://example.com/.well-known/apple-app-site-association.
  • Android App Links: Host assetlinks.json at https://example.com/.well-known/assetlinks.json containing the app’s SHA-256 fingerprint.

2. Imperative (Navigator 1.0) vs. Declarative (GoRouter)

FeatureImperative (Navigator 1.0)Declarative (GoRouter / Router API)
ModelNavigator.push() stack mutationUI = f(Location) (URL is state)
Deep Link HandlingFragile manual route parsingNative first-class URL route matching
Web NavigationPoor browser URL bar integrationPerfect browser history & URL syncing
Nested NavigationComplex nested Navigator keysShellRoute / StatefulShellRoute

3. Declarative Routing with GoRouter

GoRouter is the official Flutter package wrapping Navigator 2.0.

import 'package:go_router/go_router.dart';

final GoRouter router = GoRouter(
  initialLocation: '/',
  // Global Redirect Guard (e.g. Authentication Check)
  redirect: (context, state) {
    final bool isLoggedIn = authService.isLoggedIn;
    final bool isLoggingIn = state.matchedLocation == '/login';

    if (!isLoggedIn && !isLoggingIn) return '/login';
    if (isLoggedIn && isLoggingIn) return '/dashboard';
    return null; // No redirect needed
  },
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/login',
      builder: (context, state) => const LoginScreen(),
    ),
    // Dynamic Path Parameter Route
    GoRoute(
      path: '/product/:id',
      builder: (context, state) {
        final productId = state.pathParameters['id']!;
        return ProductDetailScreen(id: productId);
      },
    ),
  ],
);

go() vs push() in GoRouter

  • context.go('/product/123'): Replaces the navigation stack to match the exact target URI hierarchy.
  • context.push('/product/123'): Pushes a new page onto the existing navigation stack without altering URI path history.

4. Persistent Bottom Navigation (StatefulShellRoute)

StatefulShellRoute.indexedStack maintains separate navigation stacks for each tab in a bottom navigation bar, preserving scroll state and widget trees across tab switches:

StatefulShellRoute.indexedStack(
  builder: (context, state, navigationShell) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: navigationShell.currentIndex,
        onTap: (index) => navigationShell.goBranch(index),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
      ),
    );
  },
  branches: [
    StatefulShellBranch(routes: [GoRoute(path: '/home', builder: (_, __) => const HomeTab())]),
    StatefulShellBranch(routes: [GoRoute(path: '/profile', builder: (_, __) => const ProfileTab())]),
  ],
);

5. Trade-offs & Production Considerations

  • Server Hosting Dependency: If the web server hosting apple-app-site-association or assetlinks.json goes down or returns invalid JSON headers, domain verification fails, reverting deep links to browser web pages.
  • Query Parameter Validation: Deep link parameters (/product/:id?promo=xyz) come from external web links. Always sanitize and validate incoming path parameters before querying local databases or APIs.