Skip to content

Commit 4645958

Browse files
fix(build): keep web apps on path routing through boot (no /#/, deep links survive) (#6639)
* fix(build): select web URL strategy before boot, not in prepareApp() The build template called usePathUrlStrategy() from prepareApp(), which runs after the boot UI mounts. By then Flutter's web routing had already initialized under the default hash strategy, so path-routed web apps got /#/... URLs. Move the strategy selection to the first statement in main(), before FletDeepLinkingBootstrap.install() (which calls ensureInitialized) and any runApp() — matching the before-boot ordering already used in client/lib/main.dart. Necessary for path routing but not sufficient for cold-start deep links; see the follow-up boot-overlay fix. * fix(build): don't let the boot-screen MaterialApp report route '/' to the browser The boot overlay wrapped the boot screen in MaterialApp(home: ...). A MaterialApp with `home` builds its Navigator with reportsRouteUpdateToEngine: true, so on web it pushes the home route '/' to the browser URL the moment it mounts — during boot, before FletApp's real router reads the location. A cold-start deep link like /gallery or /apps/<id> was therefore overwritten to '/'. Verified with a headless-Chromium probe: pre-fix the URL reset /gallery -> / ~1.7s after 'Flutter app loaded' (before the Python worker even ran); post-fix /gallery holds. Render the boot screen via `builder:` instead of `home:`. With no home/routes/onGenerateRoute, WidgetsApp creates no Navigator (per its own docs) and reports nothing to the engine, so the deep link survives until the app's router takes over. The overlay never navigates, so it needs no Navigator. Regression from the 0.86 boot-screen rework (#6616); 0.85.3 rendered a bare SizedBox during boot, which reported nothing.
1 parent cb3ad8a commit 4645958

1 file changed

Lines changed: 24 additions & 6 deletions

File tree

  • sdk/python/templates/build/{{cookiecutter.out_dir}}/lib

sdk/python/templates/build/{{cookiecutter.out_dir}}/lib/main.dart

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ String appDir = "";
4343
Map<String, String> environmentVariables = Map.from(Platform.environment);
4444

4545
void main(List<String> args) async {
46+
// On web the URL strategy must be selected before ANYTHING reads the initial
47+
// location: FletDeepLinkingBootstrap.install() (below) calls
48+
// WidgetsFlutterBinding.ensureInitialized() and starts observing route pushes,
49+
// and the first runApp() wires up the router. Whatever strategy is active at
50+
// that point latches how the cold-start URL maps to a route — under Flutter's
51+
// default hash strategy a hard refresh of `/apps/x` reads an empty fragment
52+
// (route "/"), so the deep link is lost and the app rewrites the URL to "/".
53+
// Applying the strategy that `flet build` baked into window.flet here, as the
54+
// very first statement, keeps both the running URLs (no `/#/`) and cold-start
55+
// deep links on the path strategy.
56+
if (kIsWeb && getFletRouteUrlStrategy() == "path") {
57+
usePathUrlStrategy();
58+
}
59+
4660
FletDeepLinkingBootstrap.install();
4761

4862
_args = List<String>.from(args);
@@ -268,9 +282,16 @@ class _BootOverlayState extends State<_BootOverlay> {
268282
// resolveBootScreen is built once here (status changes update the
269283
// message via the screen's own ValueListenableBuilder), so the spinner
270284
// keeps animating across preparing → starting up.
285+
// Use `builder:` rather than `home:` so this MaterialApp creates NO
286+
// Navigator. A MaterialApp with `home` builds a Navigator with
287+
// `reportsRouteUpdateToEngine: true`, which pushes the home route "/"
288+
// to the browser URL on mount — clobbering a cold-start deep link
289+
// (e.g. `/gallery`) before FletApp's real router reads it. The boot
290+
// overlay never navigates, so it doesn't need a Navigator; this keeps
291+
// theme/Directionality while leaving the URL untouched.
271292
child: MaterialApp(
272293
debugShowCheckedModeBanner: false,
273-
home: resolveBootScreen(
294+
builder: (context, _) => resolveBootScreen(
274295
name: bootScreenName,
275296
options: bootScreenOptions,
276297
extensions: extensions,
@@ -301,12 +322,9 @@ Future prepareApp() async {
301322
);
302323

303324
if (kIsWeb) {
304-
// web mode - connect via HTTP
325+
// web mode - connect via HTTP. The URL strategy is applied in main() before
326+
// runApp(); doing it here would be too late (see the note there).
305327
pageUrl = Uri.base.toString();
306-
var routeUrlStrategy = getFletRouteUrlStrategy();
307-
if (routeUrlStrategy == "path") {
308-
usePathUrlStrategy();
309-
}
310328
assetsDir = getAssetsDir();
311329
} else if (_args.isNotEmpty && isDesktopPlatform()) {
312330
// developer mode

0 commit comments

Comments
 (0)