skills/flutter-navigation/references/web-navigation.md

3.9 KiB

Web Navigation

Use this reference when fixing Flutter web URLs, browser history, refresh/direct load behavior, server rewrites, or non-root hosting.

URL Strategies

Flutter web supports two URL strategies:

Hash Strategy

https://example.com/#/path/to/screen

Use it when you cannot configure the web server. Hash URLs avoid server rewrites because the route after # is not sent to the server.

Path Strategy

https://example.com/path/to/screen

Use it when you control server rewrites and want clean, shareable paths.

import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy();
  runApp(const App());
}

flutter_web_plugins is an SDK dependency:

dependencies:
  flutter:
    sdk: flutter
  flutter_web_plugins:
    sdk: flutter

Call usePathUrlStrategy() before runApp().

Server Rewrites

Path strategy uses the browser History API. Configure the server to serve index.html for app routes that are not real files.

Nginx

location / {
  try_files $uri $uri/ /index.html;
}

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

{
  "hosting": {
    "public": "build/web",
    "rewrites": [
      { "source": "**", "destination": "/index.html" }
    ]
  }
}

Vercel

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

Netlify

/* /index.html 200

Browser History

Router-based navigation, including go_router, integrates with the browser URL and History API for direct loads, refresh, back, and forward behavior.

Prefer canonical route state with context.go(location) for browser-visible navigation. context.push(location) is useful for page-stack style flows, but imperative navigation can be harder to reason about in browser history.

Build locations with query parameters using Uri:

context.go(
  Uri(
    path: '/search',
    queryParameters: {'q': query},
  ).toString(),
);

Hosting At A Non-Root Path

If the app is hosted at https://example.com/myapp/, update the base href in web/index.html:

<base href="/myapp/">

Keep app route paths app-relative unless the target project already includes the deployment prefix in routes:

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

Configure the hosting server so /myapp/details/42 rewrites to the built index.html for that deployed app.

Not Found And Error Routes

For public web URLs, define a deliberate not-found or error surface:

GoRouter(
  errorBuilder: (context, state) => NotFoundScreen(error: state.error),
  routes: [
    // ...
  ],
);

Validate bad routes and malformed parameters. Do not let a bad URL crash during int.parse, forced casts, or missing path parameter access.

Web Validation

When feasible, run the app in Chrome and test:

  • direct load of /;
  • direct load of every changed route;
  • refresh on every changed route;
  • browser back and forward;
  • query parameter preservation;
  • unknown URL and malformed parameter behavior;
  • deployed non-root path if applicable.

For production hosts, test after deployment as well as on the Flutter dev server. The dev server handles fallback routing automatically, while production hosting only works if rewrites are configured.

Accessibility And Titles

Navigation changes can affect focus, page announcements, and keyboard flow. After route changes, check that keyboard users can reach primary actions and that screen names, app bars, or semantic labels make the destination clear.