Chuyển đến nội dung chính

Lesson 5: Navigation & Routing

Navigator 2.0, GoRouter. Named routes, path parameters, query parameters. Nested navigation, tab navigation, drawer navigation. Deep linking, route guards.

💻 Programming — Lesson 5 Lesson 5: Navigation & Routing

Flutter & Dart: From Basics to Advanced

Part 2: Navigation & State Management

xdev.asia

1. GoRouter Setup

# pubspec.yaml
dependencies:
  go_router: ^14.0.0
final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomePage(),
    ),
    GoRoute(
      path: '/products',
      builder: (context, state) => const ProductListPage(),
      routes: [
        GoRoute(
          path: ':id',
          builder: (context, state) {
            final id = state.pathParameters['id']!;
            return ProductDetailPage(id: id);
          },
        ),
      ],
    ),
    GoRoute(
      path: '/search',
      builder: (context, state) {
        final query = state.uri.queryParameters['q'] ?? '';
        return SearchPage(query: query);
      },
    ),
  ],
  redirect: (context, state) {
    final isLoggedIn = authNotifier.isLoggedIn;
    if (!isLoggedIn && state.matchedLocation.startsWith('/dashboard')) {
      return '/login';
    }
    return null;
  },
);

2. Navigation

// Programmatic navigation
context.go('/products');
context.push('/products/123');
context.pop();

// Named routes
context.goNamed('productDetail', pathParameters: {'id': '123'});

3. Shell Route (Tab Navigation)

ShellRoute(
  builder: (context, state, child) {
    return MainShell(child: child);
  },
  routes: [
    GoRoute(path: '/home', builder: (_, __) => const HomePage()),
    GoRoute(path: '/explore', builder: (_, __) => const ExplorePage()),
    GoRoute(path: '/profile', builder: (_, __) => const ProfilePage()),
  ],
);

class MainShell extends StatelessWidget {
  final Widget child;
  const MainShell({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: NavigationBar(
        selectedIndex: _calculateIndex(GoRouterState.of(context).matchedLocation),
        destinations: const [
          NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
          NavigationDestination(icon: Icon(Icons.explore), label: 'Explore'),
          NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
        ],
        onDestinationSelected: (index) {
          switch (index) {
            case 0: context.go('/home');
            case 1: context.go('/explore');
            case 2: context.go('/profile');
          }
        },
      ),
    );
  }
}

Next article: State Management with Riverpod.