Back to skills
SkillHub ClubAnalyze Data & AIFull StackBackendData / AI

flutter-navigation

Comprehensive guide for Flutter navigation and routing including Navigator API, go_router, deep linking, passing/returning data, and web-specific navigation. Use when implementing screen transitions, configuring routing systems, setting up deep links, handling browser history, or managing navigation state in Flutter applications.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
85
Hot score
93
Updated
March 20, 2026
Overall rating
C2.6
Composite score
2.6
Best-practice grade
B77.6

Install command

npx @skill-hub/cli install madteacher-mad-agents-skills-flutter-navigation

Repository

MADTeacher/mad-agents-skills

Skill path: flutter-navigation

Comprehensive guide for Flutter navigation and routing including Navigator API, go_router, deep linking, passing/returning data, and web-specific navigation. Use when implementing screen transitions, configuring routing systems, setting up deep links, handling browser history, or managing navigation state in Flutter applications.

Open repository

Best for

Primary workflow: Analyze Data & AI.

Technical facets: Full Stack, Backend, Data / AI.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: MADTeacher.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install flutter-navigation into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/MADTeacher/mad-agents-skills before adding flutter-navigation to shared team environments
  • Use flutter-navigation for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: flutter-navigation
description: Comprehensive guide for Flutter navigation and routing including Navigator API, go_router, deep linking, passing/returning data, and web-specific navigation. Use when implementing screen transitions, configuring routing systems, setting up deep links, handling browser history, or managing navigation state in Flutter applications.
metadata:
  author: Stanislav [MADTeacher] Chernyshev
  version: "1.0"
---

# Flutter Navigation

## Overview

Implement navigation and routing in Flutter applications across mobile and web platforms. Choose the right navigation approach, configure deep linking, manage data flow between screens, and handle browser history integration.

## Choosing an Approach

### Use Navigator API (Imperative) When:
- Simple apps without deep linking requirements
- Single-screen to multi-screen transitions
- Basic navigation stacks
- Quick prototyping

Example: `assets/navigator_basic.dart`

### Use go_router (Declarative) When:
- Apps requiring deep linking (iOS, Android, Web)
- Web applications with browser history support
- Complex navigation patterns with multiple Navigator widgets
- URL-based navigation needed
- Production applications with scalable architecture

Example: `assets/go_router_basic.dart`

### Avoid Named Routes
Flutter team does NOT recommend named routes. They have limitations:
- Cannot customize deep link behavior
- No browser forward button support
- Always pushes new routes regardless of current state

## Common Tasks

### Pass Data Between Screens

**With Navigator:**
```dart
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailScreen(item: myItem)),
);
```

**With go_router:**
```dart
context.push('/details?id=123');
// Extract: final id = state.uri.queryParameters['id'];
```

Example: `assets/passing_data.dart`

### Return Data From Screens

```dart
final result = await Navigator.push(
  context,
  MaterialPageRoute<String>(builder: (context) => SelectionScreen()),
);
if (!context.mounted) return;
```

Example: `assets/returning_data.dart`

### Configure Deep Linking

**Android:** Configure `AndroidManifest.xml` intent filters
**iOS:** Configure `Info.plist` for Universal Links
**Web:** Automatic with go_router, choose URL strategy

For detailed setup: `references/deep-linking.md`

### Web URL Strategy

**Hash (default):** `example.com/#/path` - no server config needed
**Path:** `example.com/path` - cleaner URLs, requires server config

For server setup: `references/web-navigation.md`

## Navigation Methods

### go_router Navigation
- `context.go('/path')` - replace current route
- `context.push('/path')` - add to stack
- `context.pop()` - go back

### Navigator Navigation
- `Navigator.push()` - add route to stack
- `Navigator.pop()` - remove route from stack

## Advanced Topics

**Route Guards:** Implement authentication redirects
**Nested Routes:** Create shell routes with shared UI
**Error Handling:** Handle 404 and navigation errors
**Multiple Navigators:** Manage independent navigation stacks

For advanced patterns: `references/go_router-guide.md`

## Decision Guide

Use [navigation-patterns.md](references/navigation-patterns.md) for:
- Complete comparison of navigation approaches
- Deep linking behavior by platform
- Web-specific considerations
- Common patterns and anti-patterns


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/navigation-patterns.md

```markdown
# Navigation Patterns

## Choosing a Navigation Approach

### Simple Apps (No Deep Linking)
Use **Navigator API** with `Navigator.push()` and `Navigator.pop()`. Best for:
- Single-screen to multi-screen transitions
- Simple navigation stacks
- No requirement for deep linking or browser history

Example: `assets/navigator_basic.dart`

### Named Routes (Not Recommended)
Flutter team does NOT recommend named routes for most applications. Use only for:
- Very simple apps with static routes
- Basic deep linking without custom behavior

Limitations:
- Cannot customize deep link behavior
- No browser forward button support
- Always pushes new routes regardless of current state

### Complex Apps (Deep Linking, Web, Multiple Navigators)
Use **go_router** (declarative routing). Recommended for:
- Apps requiring deep linking
- Web applications with browser history
- Complex navigation patterns
- Multiple Navigator widgets
- URL-based navigation

Example: `assets/go_router_basic.dart`

## Common Patterns

### Passing Data Between Screens

**Method 1: Constructor parameters** (with Navigator)
```dart
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailScreen(item: myItem)),
);
```

Example: `assets/passing_data.dart`

**Method 2: Query parameters** (with go_router)
```dart
context.push('/details?id=123&name=test');

// Extract in screen
final id = state.uri.queryParameters['id'];
```

**Method 3: Arguments with named routes** (not recommended)
```dart
Navigator.pushNamed(context, '/details', arguments: myItem);
final item = ModalRoute.of(context)!.settings.arguments as MyItem;
```

### Returning Data From Screens

Use `await Navigator.push()`:
```dart
final result = await Navigator.push(
  context,
  MaterialPageRoute<String>(builder: (context) => SelectionScreen()),
);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$result')));
```

Example: `assets/returning_data.dart`

### Deep Linking Behavior

| Platform | Navigator | Router/go_router |
|----------|-----------|-------------------|
| iOS (not launched) | Gets initialRoute "/" then pushRoute | Gets initialRoute "/" then RouteInformationParser |
| Android (not launched) | Gets initialRoute containing route | Gets initialRoute with route path |
| iOS/Android (launched) | pushRoute called | Route parsed, Navigator configured |
| Web | No browser forward button | Full browser History API integration |

## Web-Specific Considerations

### URL Strategies

**Hash (default)**: `example.com/#/path/to/screen`
- No server configuration needed
- Works with all web servers
- URL looks less clean

**Path**: `example.com/path/to/screen`
- Requires server configuration (SPA rewrite)
- Cleaner URLs
- Use `usePathUrlStrategy()` before `runApp()`

See [web-navigation.md](web-navigation.md) for detailed setup.

### Navigation Methods

- **Navigator**: Imperative, creates pageless routes (not deep-linkable)
- **Router/go_router**: Declarative, creates page-backed routes (deep-linkable)

When using Router with Navigator together, removing a page-backed route also removes all subsequent pageless routes.

```

### references/deep-linking.md

```markdown
# Deep Linking Setup

## Overview

Deep links allow users to navigate directly to specific screens in your app from external sources like:
- Web links
- Push notifications
- Other apps
- Email links

## Platform Configuration

### Android

**Step 1: Add to `AndroidManifest.xml`**

For App Links (recommended):
```xml
<manifest>
  <application>
    <activity android:name=".MainActivity">
      <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
          android:scheme="https"
          android:host="yourapp.com" />
      </intent-filter>
    </activity>
  </application>
</manifest>
```

For custom scheme:
```xml
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="myapp" />
</intent-filter>
```

**Step 2: Verify App Links (for https scheme)**
Create `https://yourapp.com/.well-known/assetlinks.json`:
```json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.yourapp",
    "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
  }
}]
```

**Step 3: Test**
```bash
adb shell am start -W -a android.intent.action.VIEW -d "https://yourapp.com/path"
```

### iOS

**Step 1: Add to `ios/Runner/Info.plist`**

Universal Links (recommended):
```xml
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:yourapp.com</string>
</array>
```

Custom scheme:
```xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>com.example.yourapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>
```

**Step 2: Verify Universal Links**
Upload Apple App Site Association file to `https://yourapp.com/.well-known/apple-app-site-association`:
```json
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appIDs": ["TEAMID.com.example.yourapp"],
        "components": [
          {
            "/": "/path/to/*",
            "comment": "Matches anything under /path/to/"
          }
        ]
      }
    ]
  }
}
```

**Step 3: Test**
```bash
xcrun simctl openurl booted "https://yourapp.com/path"
```

## Flutter Configuration

### Using go_router (Recommended)

```dart
final _router = GoRouter(
  routes: [
    GoRoute(path: '/', builder: (context, state) => HomeScreen()),
    GoRoute(path: '/details/:id', builder: (context, state) {
      final id = state.pathParameters['id'];
      return DetailsScreen(id: id!);
    }),
    GoRoute(path: '/product/:productId', builder: (context, state) {
      final productId = state.pathParameters['productId'];
      return ProductScreen(productId: productId!);
    }),
  ],
);
```

go_router automatically handles deep links when configured correctly.

### Using Navigator (Not Recommended)

```dart
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomeScreen(),
    '/details/:id': (context) {
      final id = ModalRoute.of(context)!.settings.arguments as String;
      return DetailsScreen(id: id);
    },
  },
);
```

**Limitations:**
- Always pushes new routes (can't replace stack)
- No browser forward button on web
- Limited customization

## Web Deep Linking

Web apps handle deep links automatically. Choose URL strategy:

### Hash Strategy (Default)
```dart
// No setup needed
// URLs: https://example.com/#/path/to/screen
```

### Path Strategy
```dart
import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy();
  runApp(MyApp());
}
// URLs: https://example.com/path/to/screen
```

Configure web server to rewrite all routes to `index.html`. See [web-navigation.md](web-navigation.md).

## Testing Deep Links

### Android Emulator
```bash
adb shell am start -W -a android.intent.action.VIEW -d "myapp://product/123"
```

### iOS Simulator
```bash
xcrun simctl openurl booted "myapp://product/123"
```

### Web
Simply navigate to the URL in the browser.

## Troubleshooting

### Deep link not opening app
- Check intent filters (Android) / Info.plist (iOS)
- Verify scheme matches exactly
- Test with explicit URL

### Web 404 errors
- Configure web server for SPA
- Use hash strategy if server config not possible

### Route not found
- Ensure GoRouter path matches deep link
- Check for trailing slashes differences
- Verify parameter extraction logic

### Security warnings
- Use App Links / Universal Links instead of custom schemes
- Verify SSL certificates
- Validate deep link data

```

### references/web-navigation.md

```markdown
# Web Navigation

## URL Strategies

Flutter web supports two URL strategies for navigation:

### Hash Strategy (Default)

**URL format:** `https://example.com/#/path/to/screen`

**Advantages:**
- No server configuration needed
- Works with all web servers
- Simple setup

**Disadvantages:**
- URLs look less professional
- Share URLs contain hash

**Setup:**
```dart
// No setup required - this is the default
void main() {
  runApp(MyApp());
}
```

### Path Strategy

**URL format:** `https://example.com/path/to/screen`

**Advantages:**
- Clean, professional URLs
- Better for SEO
- More user-friendly for sharing

**Disadvantages:**
- Requires server configuration
- More complex setup

**Setup:**
```dart
import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy();
  runApp(MyApp());
}
```

**Required `pubspec.yaml`:**
```yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_web_plugins:
    sdk: flutter
```

## Server Configuration

### General SPA Rewrite Rules

All web servers must rewrite requests to `index.html` for path-based routing:

**Nginx:**
```nginx
location / {
  try_files $uri $uri/ /index.html;
}
```

**Apache (.htaccess):**
```apache
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
```

**Firebase Hosting:**
```json
{
  "hosting": {
    "public": "build/web",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "cleanUrls": true
  }
}
```

**Vercel (vercel.json):**
```json
{
  "rewrites": [
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}
```

**Netlify (_redirects file):**
```
/* /index.html 200
```

## Browser History API Integration

When using go_router or Router API, Flutter integrates with browser History API automatically:

### Forward/Back Buttons
- Browser back button works automatically
- Browser forward button works automatically
- History state is managed by go_router

### URL Updates
- URL updates when navigating
- Deep links work from external sources
- Bookmarks link to correct state

## Testing Web Navigation

### Local Development
```bash
flutter run -d chrome
# Path strategy works automatically with Flutter dev server
```

### Production
1. Build web app:
   ```bash
   flutter build web
   ```

2. Configure server with SPA rewrite rules

3. Test URLs:
   - `https://yourdomain.com/`
   - `https://yourdomain.com/details/123`
   - `https://yourdomain.com/product/456`

### Common Issues

**404 errors on navigation:**
- Configure SPA rewrite rules on server
- Check file paths are correct

**Hash still appearing:**
- Ensure `usePathUrlStrategy()` called before `runApp()`
- Check `flutter_web_plugins` is in dependencies

**Browser back button not working:**
- Use go_router instead of Navigator
- Ensure Router API is configured

## Hosting at Non-Root Path

If hosting app at subdirectory (e.g., `https://example.com/myapp/`):

### Update base href in web/index.html
```html
<base href="/myapp/">
```

### GoRouter configuration
```dart
GoRouter(
  initialLocation: '/myapp/',
  routes: [
    GoRoute(path: '/myapp/', builder: (context, state) => HomeScreen()),
    GoRoute(path: '/myapp/details', builder: (context, state) => DetailsScreen()),
  ],
);
```

## Performance Tips

1. **Use lazy loading** for route code splitting:
   ```dart
   GoRoute(
     path: '/heavy-screen',
     builder: (context, state) => HeavyScreen(),
     pageBuilder: (context, state) => MaterialPage(
       key: state.pageKey,
       child: HeavyScreen(),
     ),
   )
   ```

2. **Preload routes** for better UX:
   ```dart
   // Preload in background
   WidgetsBinding.instance.addPostFrameCallback((_) {
     GoRouter.of(context).preloadRoutes();
   });
   ```

3. **Minimize route parameters** - prefer query params for optional data

## Accessibility

- Ensure keyboard navigation works
- Test with screen readers
- Provide meaningful page titles
- Use semantic HTML when possible

```

### references/go_router-guide.md

```markdown
# go_router Guide

## Basic Setup

Add to `pubspec.yaml`:
```yaml
dependencies:
  go_router: ^17.0.0
```

## Simple Configuration

```dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final _router = GoRouter(
  routes: [
    GoRoute(path: '/', builder: (context, state) => HomeScreen()),
    GoRoute(path: '/details', builder: (context, state) => DetailsScreen()),
  ],
);

void main() {
  runApp(MaterialApp.router(routerConfig: _router));
}
```

## Navigation Methods

### Declarative Navigation

**Go to screen (replace current):**
```dart
context.go('/details');
```

**Push screen (add to stack):**
```dart
context.push('/details');
```

**Pop screen (go back):**
```dart
context.pop();
// OR return data
context.pop('result_value');
```

### Named Routes with go_router

```dart
GoRoute(
  name: 'details',
  path: '/details/:id',
  builder: (context, state) {
    final id = state.pathParameters['id'];
    return DetailsScreen(id: id!);
  },
);

// Navigate using name
context.goNamed('details', pathParameters: {'id': '123'});
// With query parameters
context.goNamed('details', 
  pathParameters: {'id': '123'},
  queryParameters: {'tab': 'info'},
);
```

## Passing Data

### Path Parameters
```dart
GoRoute(path: '/users/:userId', builder: (context, state) {
  final userId = state.pathParameters['userId'];
  return UserDetailScreen(userId: userId!);
});

context.push('/users/123');
```

### Query Parameters
```dart
GoRoute(path: '/search', builder: (context, state) {
  final query = state.queryParameters['q'];
  final page = state.queryParameters['page'];
  return SearchScreen(query: query, page: int.tryParse(page ?? '1'));
});

context.push('/search?q=flutter&page=2');
// OR using queryParameters parameter
context.push('/search', queryParameters: {'q': 'flutter', 'page': '2'});
```

### Extra Data
```dart
GoRoute(path: '/details', builder: (context, state) {
  final extra = state.extra as Map<String, dynamic>?;
  return DetailsScreen(data: extra);
});

context.push('/details', extra: {'key': 'value'});
// Can combine with query parameters
context.push('/details', 
  extra: {'key': 'value'},
  queryParameters: {'id': '123'},
);
```

## Advanced Patterns

### Nested Routes (Shell Routes)

```dart
ShellRoute(
  builder: (context, state, child) {
    return Scaffold(
      appBar: AppBar(title: const Text('App')),
      body: child,
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
        ],
        onTap: (index) {
          if (index == 0) context.go('/home');
          if (index == 1) context.go('/settings');
        },
      ),
    );
  },
  routes: [
    GoRoute(path: '/home', builder: (context, state) => HomeScreen()),
    GoRoute(path: '/settings', builder: (context, state) => SettingsScreen()),
  ],
);
```

### Route Guards (Redirects)

```dart
GoRouter(
  redirect: (context, state) {
    final isAuthenticated = checkAuth();
    final isLoggingIn = state.matchedLocation == '/login';

    if (!isAuthenticated && !isLoggingIn) {
      return '/login';
    }
    if (isAuthenticated && isLoggingIn) {
      return '/';
    }
    return null; // no redirect
  },
  routes: [...],
);
```

### Error Handling

```dart
GoRouter(
  errorBuilder: (context, state) => ErrorScreen(error: state.error),
  routes: [...],
);
```

## Deep Linking

go_router automatically handles deep linking. Ensure platform setup:

**Android** (`AndroidManifest.xml`):
```xml
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" android:host="yourapp.com" />
</intent-filter>
```

**iOS** (`Info.plist`):
```xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourapp</string>
    </array>
  </dict>
</array>
```

## Common Pitfalls

1. **Don't use `Navigator.push/pop` directly** when using go_router for main navigation
2. **Always check `context.mounted`** after async navigation operations
3. **Use `context.push()`** for adding to stack, `context.go()` for replacing
4. **Path parameters are mandatory**, query parameters are optional
5. **Web browser back button works automatically** with go_router
6. **API changes in v7.0.0+**: Use `pathParameters`/`queryParameters` instead of `params`/`queryParams`, and `matchedLocation` instead of `subloc`
7. **Use `ShellRoute`** for nested navigation with persistent UI, not nested `GoRoute` with `Navigator`
```

flutter-navigation | SkillHub