flutter-setup-declarative-routing

Configure `MaterialApp.router` using a package like `go_router` for advanced URL-based navigation. Use when developing web applications or mobile apps that…

INSTALLATION
npx skills add https://github.com/flutter/skills --skill flutter-setup-declarative-routing
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

Follow this workflow to bootstrap a new Flutter application with go_router and configure the root routing mechanism.

Task Progress

  • Create the Flutter application.
  • Add the go_router dependency.
  • Configure the URL strategy for web/deep linking.
  • Implement the GoRouter configuration.
  • Bind the router to MaterialApp.router.

1. Scaffold the Application

Run the following commands to create the app and add the required routing package:

flutter create <app-name>

cd <app-name>

flutter pub add go_router

2. Configure the Router

Define a top-level GoRouter instance. Handle authentication or state-based routing using the redirect parameter.

import 'package:flutter/material.dart';

import 'package:go_router/go_router.dart';

import 'package:flutter_web_plugins/url_strategy.dart';

void main() {

  // Use path URL strategy to remove the '#' from web URLs

  usePathUrlStrategy();

  runApp(const MyApp());

}

final GoRouter _router = GoRouter(

  initialLocation: '/',

  routes: [

    GoRoute(

      path: '/',

      builder: (context, state) => const HomeScreen(),

      routes: [

        GoRoute(

          path: 'details/:id',

          builder: (context, state) => DetailsScreen(id: state.pathParameters['id']!),

        ),

      ],

    ),

  ],

  errorBuilder: (context, state) => ErrorScreen(error: state.error),

);

class MyApp extends StatelessWidget {

  const MyApp({super.key});

  @override

  Widget build(BuildContext context) {

    return MaterialApp.router(

      routerConfig: _router,

      title: 'Routing App',

    );

  }

}

Workflow: Configuring Platform Deep Linking

Configure the native platforms to intercept specific URLs and route them into the Flutter application.

Task Progress

  • Determine target platforms (iOS, Android, or both).
  • Apply conditional configuration for Android (Manifest + Asset Links).
  • Apply conditional configuration for iOS (Plist + Entitlements + AASA).
  • Run validator -> review errors -> fix.

If configuring for Android:

  • **Modify AndroidManifest.xml**: Add the intent filter inside the <activity> tag for .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="http" android:host="yourdomain.com" />

    <data android:scheme="https" />

</intent-filter>
  • **Host assetlinks.json**: Serve the following JSON at https://yourdomain.com/.well-known/assetlinks.json.
[{

  "relation": ["delegate_permission/common.handle_all_urls"],

  "target": {

    "namespace": "android_app",

    "package_name": "com.yourcompany.yourapp",

    "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]

  }

}]

If configuring for iOS:

  • **Modify Info.plist**: Opt-in to Flutter's default deep link handler.

Note: If using a third-party deep linking plugin (e.g., app_links), set this to NO to prevent conflicts.

<key>FlutterDeepLinkingEnabled</key>

<true/>
  • **Modify Runner.entitlements**: Add the associated domain.
<key>com.apple.developer.associated-domains</key>

<array>

  <string>applinks:yourdomain.com</string>

</array>
  • **Host apple-app-site-association**: Serve the following JSON (without a .json extension) at https://yourdomain.com/.well-known/apple-app-site-association.
{

  "applinks": {

    "apps": [],

    "details": [{

      "appIDs": ["TEAM_ID.com.yourcompany.yourapp"],

      "paths": ["*"],

      "components": [{"/": "/*"}]

    }]

  }

}

Validation Loop

Run validator -> review errors -> fix.

  • Android: Test using ADB.
adb shell 'am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://yourdomain.com/details/123"' com.yourcompany.yourapp
  • iOS: Test using xcrun on a booted simulator.
xcrun simctl openurl booted https://yourdomain.com/details/123

Workflow: Implementing Nested Navigation

Use StatefulShellRoute to implement persistent UI shells (like a bottom navigation bar) that maintain the state of their child routes.

Task Progress

  • Define StatefulShellRoute.indexedStack in the GoRouter configuration.
  • Create StatefulShellBranch instances for each navigation tab.
  • Implement the shell widget using StatefulNavigationShell.
final GoRouter _router = GoRouter(

  initialLocation: '/home',

  routes: [

    StatefulShellRoute.indexedStack(

      builder: (context, state, navigationShell) {

        return ScaffoldWithNavBar(navigationShell: navigationShell);

      },

      branches: [

        StatefulShellBranch(

          routes: [

            GoRoute(

              path: '/home',

              builder: (context, state) => const HomeScreen(),

            ),

          ],

        ),

        StatefulShellBranch(

          routes: [

            GoRoute(

              path: '/settings',

              builder: (context, state) => const SettingsScreen(),

            ),

          ],

        ),

      ],

    ),

  ],

);

Examples

High-Fidelity Shell Widget Implementation

Implement the UI shell that consumes the StatefulNavigationShell to handle branch switching.

class ScaffoldWithNavBar extends StatelessWidget {

  const ScaffoldWithNavBar({

    required this.navigationShell,

    super.key,

  });

  final StatefulNavigationShell navigationShell;

  void _goBranch(int index) {

    navigationShell.goBranch(

      index,

      // Support navigating to the initial location when tapping the active tab.

      initialLocation: index == navigationShell.currentIndex,

    );

  }

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      body: navigationShell,

      bottomNavigationBar: NavigationBar(

        selectedIndex: navigationShell.currentIndex,

        onDestinationSelected: _goBranch,

        destinations: const [

          NavigationDestination(icon: Icon(Icons.home), label: 'Home'),

          NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'),

        ],

      ),

    );

  }

}

Programmatic Navigation

Use the context.go() and context.push() extension methods provided by go_router.

// Replaces the current route stack with the target route (Declarative)

context.go('/details/123');

// Pushes the target route onto the existing stack (Imperative)

context.push('/details/123');

// Navigates using a named route and path parameters

context.goNamed('details', pathParameters: {'id': '123'});

// Pops the current route

context.pop();
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card