Claude
Skills
Sign in
Back

custom-plugin-flutter-skill-navigation

Included with Lifetime
$97 forever

Production-grade Flutter navigation mastery - Navigator 2.0, GoRouter, deep linking, nested navigation, route guards, web URL handling with comprehensive code examples

Generalscriptsassets

What this skill does


# custom-plugin-flutter: Navigation Skill

## Quick Start - GoRouter Production Setup

```dart
// router.dart
final router = GoRouter(
  initialLocation: '/',
  debugLogDiagnostics: true,
  refreshListenable: authNotifier,
  redirect: (context, state) {
    final isLoggedIn = authNotifier.isLoggedIn;
    final isLoggingIn = state.matchedLocation == '/login';

    if (!isLoggedIn && !isLoggingIn) return '/login';
    if (isLoggedIn && isLoggingIn) return '/';
    return null;
  },
  errorBuilder: (context, state) => ErrorPage(error: state.error),
  routes: [
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomePage(),
    ),
    GoRoute(
      path: '/login',
      name: 'login',
      builder: (context, state) => const LoginPage(),
    ),
    GoRoute(
      path: '/products/:id',
      name: 'product',
      builder: (context, state) {
        final id = state.pathParameters['id']!;
        return ProductPage(productId: id);
      },
    ),
    ShellRoute(
      builder: (context, state, child) => MainShell(child: child),
      routes: [
        GoRoute(path: '/dashboard', builder: (_, __) => DashboardPage()),
        GoRoute(path: '/settings', builder: (_, __) => SettingsPage()),
      ],
    ),
  ],
);

// main.dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
    );
  }
}

// Navigation usage
context.go('/products/123');                    // Replace
context.push('/products/123');                  // Push
context.goNamed('product', pathParameters: {'id': '123'});
context.pop();                                  // Go back
```

## 1. Navigator 1.0 (Imperative)

### Basic Navigation

```dart
// Push new route
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailPage()),
);

// Pop current route
Navigator.pop(context);

// Pop with result
Navigator.pop(context, 'result_data');

// Push and remove until
Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(builder: (_) => HomePage()),
  (route) => false,  // Remove all
);

// Push replacement
Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (_) => NewPage()),
);
```

### Named Routes

```dart
// Define routes
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomePage(),
    '/detail': (context) => DetailPage(),
    '/settings': (context) => SettingsPage(),
  },
  onGenerateRoute: (settings) {
    if (settings.name?.startsWith('/product/') ?? false) {
      final id = settings.name!.split('/').last;
      return MaterialPageRoute(
        builder: (_) => ProductPage(id: id),
      );
    }
    return null;
  },
  onUnknownRoute: (settings) {
    return MaterialPageRoute(builder: (_) => NotFoundPage());
  },
);

// Navigate with arguments
Navigator.pushNamed(
  context,
  '/detail',
  arguments: {'id': 123, 'title': 'Product'},
);

// Retrieve arguments
final args = ModalRoute.of(context)?.settings.arguments as Map?;
```

## 2. Navigator 2.0 (Declarative)

### Router Configuration

```dart
class AppRouterDelegate extends RouterDelegate<AppRoutePath>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoutePath> {
  @override
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  AppRoutePath _currentPath = AppRoutePath.home();

  @override
  AppRoutePath get currentConfiguration => _currentPath;

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
          key: ValueKey('home'),
          child: HomePage(),
        ),
        if (_currentPath.isProductPage)
          MaterialPage(
            key: ValueKey('product-${_currentPath.productId}'),
            child: ProductPage(id: _currentPath.productId!),
          ),
      ],
      onPopPage: (route, result) {
        if (!route.didPop(result)) return false;
        _currentPath = AppRoutePath.home();
        notifyListeners();
        return true;
      },
    );
  }

  @override
  Future<void> setNewRoutePath(AppRoutePath path) async {
    _currentPath = path;
    notifyListeners();
  }

  void goToProduct(String id) {
    _currentPath = AppRoutePath.product(id);
    notifyListeners();
  }
}

class AppRouteInformationParser extends RouteInformationParser<AppRoutePath> {
  @override
  Future<AppRoutePath> parseRouteInformation(
    RouteInformation routeInformation,
  ) async {
    final uri = routeInformation.uri;

    if (uri.pathSegments.isEmpty) {
      return AppRoutePath.home();
    }

    if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'product') {
      return AppRoutePath.product(uri.pathSegments[1]);
    }

    return AppRoutePath.home();
  }

  @override
  RouteInformation restoreRouteInformation(AppRoutePath path) {
    if (path.isHomePage) {
      return RouteInformation(uri: Uri.parse('/'));
    }
    if (path.isProductPage) {
      return RouteInformation(uri: Uri.parse('/product/${path.productId}'));
    }
    return RouteInformation(uri: Uri.parse('/'));
  }
}
```

## 3. GoRouter Advanced Patterns

### Nested Navigation with ShellRoute

```dart
final router = GoRouter(
  routes: [
    ShellRoute(
      navigatorKey: _shellNavigatorKey,
      builder: (context, state, child) {
        return ScaffoldWithNavBar(child: child);
      },
      routes: [
        GoRoute(
          path: '/home',
          pageBuilder: (context, state) => NoTransitionPage(
            child: HomePage(),
          ),
        ),
        GoRoute(
          path: '/search',
          pageBuilder: (context, state) => NoTransitionPage(
            child: SearchPage(),
          ),
        ),
        GoRoute(
          path: '/profile',
          pageBuilder: (context, state) => NoTransitionPage(
            child: ProfilePage(),
          ),
        ),
      ],
    ),
    // Routes outside shell (full screen)
    GoRoute(
      path: '/product/:id',
      parentNavigatorKey: _rootNavigatorKey,
      builder: (context, state) => ProductPage(
        id: state.pathParameters['id']!,
      ),
    ),
  ],
);

class ScaffoldWithNavBar extends StatelessWidget {
  final Widget child;
  const ScaffoldWithNavBar({required this.child});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: NavigationBar(
        selectedIndex: _calculateSelectedIndex(context),
        onDestinationSelected: (index) => _onItemTapped(index, context),
        destinations: [
          NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
          NavigationDestination(icon: Icon(Icons.search), label: 'Search'),
          NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
        ],
      ),
    );
  }

  int _calculateSelectedIndex(BuildContext context) {
    final location = GoRouterState.of(context).matchedLocation;
    if (location.startsWith('/home')) return 0;
    if (location.startsWith('/search')) return 1;
    if (location.startsWith('/profile')) return 2;
    return 0;
  }

  void _onItemTapped(int index, BuildContext context) {
    switch (index) {
      case 0: context.go('/home');
      case 1: context.go('/search');
      case 2: context.go('/profile');
    }
  }
}
```

### Route Guards & Redirects

```dart
GoRouter(
  redirect: (context, state) {
    final isLoggedIn = ref.read(authProvider).isLoggedIn;
    final isOnboarded = ref.read(onboardingProvider).completed;
    final location = state.matchedLocation;

    // Onboarding flow
    if (!isOnboarded && !location.startsWith('/onboarding')) {
      return '/onboarding';
    }

    // Auth flow
    if (!isLoggedIn) {
      final publicRoutes = ['/login', '/register', '/forgot-password'];
      if (!publicRoutes.contains(location)) {
        return '/login?redirect=${Uri.encodeComponent(location)}';
      }
    }

    // Already logged in, redirect away from auth pages
    if (isLoggedIn && location == '/logi

Related in General