Question #9MediumFlutter Basics

Routes in flutter

#flutter#route

Answer

Overview

Routes in Flutter represent different screens or pages in your app. Route management allows navigation between these screens, similar to URLs in web development.


Types of Routes

1. Named Routes

Routes defined with string identifiers in a central location.

dart
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomeScreen(),
    '/profile': (context) => ProfileScreen(),
    '/settings': (context) => SettingsScreen(),
  },
)

Navigation:

dart
// Push
Navigator.pushNamed(context, '/profile');

// Pop
Navigator.pop(context);

// Replace
Navigator.pushReplacementNamed(context, '/home');

Pros:

  • ✅ Centralized route management
  • ✅ Easy to maintain
  • ✅ Clean navigation code

Cons:

  • ❌ Harder to pass parameters
  • ❌ No type safety

2. Anonymous Routes

Routes created on-the-fly using MaterialPageRoute.

dart
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => ProfileScreen(userId: 123),
  ),
);

Pros:

  • ✅ Easy to pass parameters
  • ✅ Type-safe
  • ✅ Flexible

Cons:

  • ❌ Decentralized (routes scattered across code)
  • ❌ Harder to track navigation flow

3. Generated Routes

Routes generated programmatically with onGenerateRoute.

dart
MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == '/profile') {
      final args = settings.arguments as Map<String, dynamic>;
      return MaterialPageRoute(
        builder: (context) => ProfileScreen(userId: args['id']),
      );
    }
    return null;
  },
)

Pros:

  • ✅ Type-safe parameter passing
  • ✅ Centralized route logic
  • ✅ Custom route handling

Navigation Methods

Push (Add to Stack)

dart
// Named route
Navigator.pushNamed(context, '/settings');

// Anonymous route
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SettingsScreen()),
);

Pop (Remove from Stack)

dart
// Go back
Navigator.pop(context);

// Go back with result
Navigator.pop(context, {'success': true});

Push Replacement

Replace current route with new one.

dart
Navigator.pushReplacementNamed(context, '/home');

Push and Remove Until

Navigate and clear stack until condition.

dart
// Clear all and go to home
Navigator.pushNamedAndRemoveUntil(
  context,
  '/home',
  (route) => false,
);

// Keep only first route
Navigator.pushNamedAndRemoveUntil(
  context,
  '/success',
  ModalRoute.withName('/'),
);

Passing Data Between Routes

Method 1: Constructor (Anonymous Routes)

dart
// Navigate with data
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => ProfileScreen(
      userId: 123,
      userName: 'John',
    ),
  ),
);

// Receive data
class ProfileScreen extends StatelessWidget {
  final int userId;
  final String userName;
  
  const ProfileScreen({required this.userId, required this.userName});
  
  
  Widget build(BuildContext context) {
    return Text('User: $userName');
  }
}

Method 2: Arguments (Named Routes)

dart
// Navigate with arguments
Navigator.pushNamed(
  context,
  '/profile',
  arguments: {'id': 123, 'name': 'John'},
);

// Receive arguments
class ProfileScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as Map;
    return Text('User: ${args['name']}');
  }
}

Method 3: Return Data (Pop with Result)

dart
// Screen 1: Wait for result
final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SelectionScreen()),
);
print('Selected: $result');

// Screen 2: Return result
Navigator.pop(context, 'Selected Item');

Complete Example

dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Routes Demo',
      initialRoute: '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/details': (context) => DetailsScreen(),
      },
      onGenerateRoute: (settings) {
        // Handle dynamic routes
        if (settings.name == '/profile') {
          final args = settings.arguments as Map;
          return MaterialPageRoute(
            builder: (context) => ProfileScreen(userId: args['id']),
          );
        }
        return null;
      },
      onUnknownRoute: (settings) {
        // 404 page
        return MaterialPageRoute(builder: (context) => NotFoundScreen());
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () => Navigator.pushNamed(context, '/details'),
            child: Text('Go to Details'),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(
                context,
                '/profile',
                arguments: {'id': 123},
              );
            },
            child: Text('Go to Profile'),
          ),
        ],
      ),
    );
  }
}

class DetailsScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.pop(context),
          child: Text('Go Back'),
        ),
      ),
    );
  }
}

Route Transitions

Custom Page Route

dart
class FadeRoute extends PageRouteBuilder {
  final Widget page;
  
  FadeRoute({required this.page})
      : super(
          pageBuilder: (context, animation, secondaryAnimation) => page,
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            return FadeTransition(opacity: animation, child: child);
          },
        );
}

// Usage
Navigator.push(context, FadeRoute(page: ProfileScreen()));

Best Practices

Tip: Use named routes for large apps, anonymous routes for small apps

  • ✅ Use named routes for maintainability
  • ✅ Keep route names as constants
  • ✅ Handle unknown routes with onUnknownRoute
  • ✅ Use type-safe arguments
  • ✅ Avoid deep navigation stacks
  • ❌ Don't mix navigation approaches
dart
// Good - Route constants
class Routes {
  static const home = '/';
  static const profile = '/profile';
  static const settings = '/settings';
}

Navigator.pushNamed(context, Routes.profile);

Resources