Home
Dart
OOP
Flutter
Contact

Navigation and Routing

Master screen transitions, named routes, and passing data between pages.

Named Routes

Define routes in MaterialApp and navigate by name:

app.dart
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomeScreen(),
    '/products': (context) => const ProductListScreen(),
    '/settings': (context) => const SettingsScreen(),
  },
)

// Navigate by name
Navigator.pushNamed(context, '/products');

Passing Arguments

Use onGenerateRoute to handle complex route arguments:

routes.dart
class AppRouter {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case '/product-detail':
        final args = settings.arguments as ProductArgs;
        return MaterialPageRoute(
          builder: (_) => ProductDetailScreen(product: args.product),
        );
      case '/settings':
        return MaterialPageRoute(builder: (_) => const SettingsScreen());
      default:
        return MaterialPageRoute(builder: (_) => const HomeScreen());
    }
  }
}

// Navigate with arguments
Navigator.pushNamed(context, '/product-detail',
  arguments: ProductArgs(product: selectedProduct),
);

Real-World Example: Product List → Detail

product_flow.dart
// Product List Screen
class ProductListScreen extends StatelessWidget {
  const ProductListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Products')),
      body: ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text('Product ${index + 1}'),
            subtitle: Text('\$${(index + 1) * 9.99}'),
            trailing: const Icon(Icons.chevron_right),
            onTap: () => Navigator.pushNamed(
              context, '/product-detail',
              arguments: ProductArgs(
                product: Product('p_${index}', 'Product ${index + 1}', (index + 1) * 9.99),
              ),
            ),
          );
        },
      ),
    );
  }
}

// Product Detail Screen
class ProductDetailScreen extends StatelessWidget {
  final Product product;
  const ProductDetailScreen({super.key, required this.product});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(product.name)),
      body: Column(children: [
        Text(product.name, style: Theme.of(context).textTheme.headlineMedium),
        Text('\$${product.price.toStringAsFixed(2)}',
          style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        ElevatedButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('← Back to Products'),
        ),
      ]),
    );
  }
}

Summary

  • Navigator.push/pop for imperative navigation
  • Named routes for cleaner route definitions
  • onGenerateRoute for complex argument passing
  • Navigator 2.0 for deep linking and declarative routing
🚀 Next Step Continue to Local Data Persistence.
K
Kesavaraja Murugesan
Flutter Developer & Educator

Flutter developer with 3 years of experience building production-grade mobile applications. Passionate about teaching clean architecture patterns and helping developers write maintainable, scalable code.

← State Management Next: Local Storage →