Implementing Advanced Navigation
1. Using Navigator 2.0
| Property | Description | Values |
|---|---|---|
| initialRoute | Initial route to navigate to when the app starts | /home |
| onGenerateRoute | Function to generate a route based on the route name | (RouteSettings settings) => Route |
| onUnknownRoute | Function to handle unknown routes | (RouteSettings settings) => Route |
Example: Basic Navigator 2.0 Setup
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name == '/') {
return MaterialPageRoute(
builder: (context) => HomeScreen(),
);
}
return null;
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(child: Text('Welcome to Home Screen')),
);
}
}
onGenerateRoute or onUnknownRoute to prevent runtime errors.
2. Using RouterDelegate and RouteInformationParser
| Class | Description | Usage |
|---|---|---|
| RouterDelegate | Manages navigation state and builds the UI for a given route. | Implement to control how routes are displayed and updated. |
| RouteInformationParser | Parses route information from the URL and converts it into a RouteSettings object. | Used to handle navigation based on URL changes in web apps. |
Example: Implementing RouterDelegate
class MyRouterDelegate extends RouterDelegate<String> {
final String initialRoute;
MyRouterDelegate(this.initialRoute);
@override
Future<void> setNewRoutePath(String configuration) async {
// Handle navigation to a new route
}
@override
Future<void> popRoute() async {
// Handle popping a route
}
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: this,
routeInformationParser: MyRouteInformationParser(),
);
}
}
Example: Implementing RouteInformationParser
class MyRouteInformationParser extends RouteInformationParser<String> {
@override
Future<String> parseRouteInformation(RouteInformation routeInformation) async {
return routeInformation.location ?? '/';
}
@override
RouteInformation restoreRouteInformation(String configuration) {
return RouteInformation(location: configuration);
}
}
3. Implementing Deep Linking
Deep linking in Flutter allows navigation to specific screens or states within an app using URLs or custom schemes. This is particularly useful for sharing content, integrating with external services, or enabling direct access to app features.
| Feature | Description | Implementation |
|---|---|---|
| Deep Link URL | URL that maps to a specific screen or state in the app. | myapp://screen1?param=value |
| Custom Scheme | App-specific URL scheme to handle deep links. | myapp:// |
| Universal Links (iOS) | URLs that work both on iOS and web, using HTTPS. | https://myapp.com/screen1 |
| App Links (Android) | URLs that work on Android devices using HTTPS. | https://myapp.com/screen1 |
Example: Handling Deep Links in Flutter
import 'package:flutter/material.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: (settings) {
// Handle deep links here
if (settings.name == '/screen1') {
return MaterialPageRoute(
builder: (context) => Screen1(),
);
}
return null;
},
);
}
}
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Screen 1')),
body: Center(child: Text('Deep Linked Screen')),
);
}
}
Info.plist includes the custom URL scheme. On Android, configure AndroidManifest.xml to handle intent filters.
Deep Linking Setup Steps
- Define custom URL schemes in app configuration.
- Implement route handling logic for incoming URLs.
- Test deep links on both iOS and Android devices.
4. Using go_router Package
| Feature | Description | Usage |
|---|---|---|
| go_router | Modern, flexible routing solution for Flutter apps | GoRouter class used to define routes and navigation logic |
| routes | List of route definitions | Each route is defined with name, path, and builder |
| go | Method to navigate to a route | context.go('/home') |
| goNamed | Navigate to a named route | context.goNamed('profile') |
| pop | Go back to the previous route | context.pop() |
Example: Basic go_router setup
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
final router = GoRouter(
routes: [
GoRoute(
name: 'home',
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
name: 'profile',
path: '/profile',
builder: (context, state) => ProfileScreen(),
),
],
);
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () => context.goNamed('profile'),
child: Text('Go to Profile'),
),
),
);
}
}
Example: Navigation with parameters
final router = GoRouter(
routes: [
GoRoute(
name: 'user',
path: '/user/:id',
builder: (context, state) {
final userId = state.pathParameters['id']!;
return UserScreen(userId: userId);
},
),
],
);
class UserScreen extends StatelessWidget {
final String userId;
UserScreen({required this.userId});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('User $userId')),
body: Center(child: Text('User ID: $userId')),
);
}
}
5. Using auto_route Package
| Feature | Description | Notes |
|---|---|---|
| auto_route | ^7.8.0 |
Latest stable version as of 2026 |
Example: Setting up auto_route
// pubspec.yaml
dependencies:
auto_route: ^7.8.0
dev_dependencies:
auto_route_generator: ^7.8.0
Example: Defining Routes
@MaterialAutoRouter(
routes: <AutoRoute>[
AutoRoute(page: HomeScreen, initial: true),
AutoRoute(page: ProfileScreen),
AutoRoute(page: SettingsScreen),
],
)
class AppRouter {}
Example: Navigating with auto_route
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () {
context.pushRoute(ProfileScreenRoute());
},
child: Text('Go to Profile'),
),
),
);
}
}
auto_route package provides compile-time route generation, improving performance and type safety.
@MaterialAutoRouter or @CupertinoAutoRouter.
Key Features of auto_route
- Compile-time route generation
- Type-safe navigation
- Supports nested routes
- Lazy loading support
- Deep linking capabilities
6. Implementing Nested Navigation
Nested navigation in Flutter allows you to manage complex navigation hierarchies, such as having a bottom navigation bar with its own set of routes. This is particularly useful for apps that have multiple levels of navigation within a single screen or tab.
| Property | Description | Example |
|---|---|---|
| Navigator.pushNamed | Pushes a new route onto the navigation stack using a named route. | Navigator.pushNamed(context, '/nestedRoute'); |
| Navigator.pop | Pops the current route off the navigation stack. | Navigator.pop(context); |
| Navigator.pushReplacementNamed | Replaces the current route with a new one using a named route. | Navigator.pushReplacementNamed(context, '/newRoute'); |
Example: Nested Navigation with Bottom Navigation
class MainScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Main Screen')),
body: Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => NestedScreen(),
);
},
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
onTap: (index) {
if (index == 0) {
Navigator.pushNamed(context, '/home');
} else {
Navigator.pushNamed(context, '/settings');
}
},
),
);
}
}
Example: Nested Route with TabBar
class NestedScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('Nested Navigation'),
bottom: TabBar(
tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
],
),
),
body: TabBarView(
children: [
Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => TabOneScreen(),
);
},
),
Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => TabTwoScreen(),
);
},
),
],
),
),
);
}
}
7. Creating Custom Route Transitions
| Property | Description | Values |
|---|---|---|
| pageBuilder | Function to build the page widget | (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Widget |
| transitionDuration | Duration of the transition animation | Duration |
| reverseTransitionDuration | Duration of the reverse transition animation | Duration |
Example: Custom Slide Transition
class SlideTransitionPage extends PageRouteBuilder {
final Widget page;
SlideTransitionPage({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child,
);
},
);
}
Example: Fade Transition
class FadeTransitionPage extends PageRouteBuilder {
final Widget page;
FadeTransitionPage({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
child: child,
);
},
);
}
8. Using PageRouteBuilder Widget
The PageRouteBuilder is a powerful widget in Flutter that allows you to create custom page transitions and animations. It provides fine-grained control over how a route is built and animated.
| Property | Description | Values |
|---|---|---|
| pageBuilder | Function that builds the page widget | (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Widget |
| transitionBuilder | Function that builds the transition animation | (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) => Widget |
| duration | Duration of the transition animation | Duration |
| reverseDuration | Duration of the reverse transition animation | Duration |
| fullscreenDialog | Whether the route is a full-screen dialog | bool |
Example: Custom Page Transition
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return MyPage();
},
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
duration: Duration(milliseconds: 300),
)
PageRouteBuilder is particularly useful for creating complex custom transitions that are not available through the built-in route builders.
9. Implementing Hero Animations
Hero animations in Flutter provide a seamless transition between screens by animating a widget from one screen to another. This is particularly useful for navigating between detail and list views.
| Property | Description | Values |
|---|---|---|
| tag | Unique identifier for the Hero widget | String |
| child | The widget to animate | Widget |
| transitionOnUserGestures | Whether to allow user gestures to affect the transition | bool |
Example: Basic Hero Animation
import 'package:flutter/material.dart';
class HeroExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hero Example')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HeroDetailScreen(),
),
);
},
child: Hero(
tag: 'heroImage',
child: Image.asset('assets/images/hero.png'),
),
),
),
);
}
}
class HeroDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hero Detail')),
body: Center(
child: Hero(
tag: 'heroImage',
child: Image.asset('assets/images/hero.png'),
),
),
);
}
}
Example: Hero with Custom Transition
class CustomHeroTransition extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Custom Hero')),
body: Center(
child: Hero(
tag: 'customHero',
transitionOnUserGestures: true,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
),
);
}
}
tag must be unique within the navigation stack to ensure proper animation matching.
Navigator instances. Ensure all widgets are within the same navigation stack.
Hero Animation Best Practices
- Use unique tags for each Hero widget to ensure proper matching.
- Ensure the
childwidgets have similar shapes and sizes for smooth transitions. - Use
transitionOnUserGesturesto allow user interaction during animation. - Avoid Hero animations in complex layouts where widget sizes may vary significantly.
10. Managing Navigation Stack Programmatically
| Method | Description | Usage |
|---|---|---|
| push | Adds a new route to the top of the navigation stack | navigator.push(MaterialPageRoute(builder: (context) => NewScreen())); |
| pop | Removes the current route from the stack and navigates back | navigator.pop(); |
| popUntil | Pops routes until a predicate route is found | navigator.popUntil(ModalRoute.withName('/')); |
| replaceAll | Replaces all routes in the stack with a new list of routes | navigator.pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomeScreen()), ModalRoute.withName('/')); |
| removeRoute | Removes a specific route from the stack | navigator.removeRoute(route); |
Navigator.of(context) to access the current navigator instance.11. Implementing Bottom Navigation with Routes
| Property | Description | Example |
|---|---|---|
| BottomNavigationBar | Widget for creating bottom navigation bar | BottomNavigationBar(items: [ ... ]) |
| BottomNavigationBarItem | Represents a single item in the bottom nav bar | BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home') |
| currentIndex | Index of the currently selected item | currentIndex: _selectedIndex |
| onTap | Callback when an item is tapped | onTap: (index) { setState(() { _selectedIndex = index; }); } |
Example: Basic Bottom Navigation Setup
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
final List<Widget> _pages = [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bottom Navigation Example')),
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Home Screen'));
}
}
class SearchScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Search Screen'));
}
}
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Profile Screen'));
}
}
currentIndex must be updated via setState to reflect the selected item.
_pages list corresponds to the correct index in BottomNavigationBarItem.