From 612f28a6650d47d41c79de7f0ca9d56ce1d06664 Mon Sep 17 00:00:00 2001 From: Nadeem Iqbal Date: Sun, 17 May 2026 23:16:43 +0500 Subject: [PATCH 1/2] [go_router] Add regression test for synchronous onEnter Block.then(router.go) A synchronous onEnter callback that returns `Block.then(() => router.go(...))` on the initial route must still run the `then` redirect. This was the user-visible symptom in flutter/flutter#179370: replacing `async` with a synchronous callback caused the initial redirect to be silently lost, which the reporter worked around by re-adding `async`. The underlying re-entrancy bug was fixed by #11136 (replacing `await Future.sync(callback)` with `scheduleMicrotask(() async { await callback(); })`). Lock in that behavior with a regression test mirroring the exact predicate from the report (`current.uri.path == '/dashboard'`), which on the initial parse matches because the router has no committed configuration yet. Verified that reverting `parser.dart` to the pre-#11136 implementation makes this test fail; with the current implementation it passes alongside the rest of `on_enter_test.dart` (32/32). Fixes flutter/flutter#179370. --- packages/go_router/test/on_enter_test.dart | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/go_router/test/on_enter_test.dart b/packages/go_router/test/on_enter_test.dart index 2fb09cc051f3..133dc708a32a 100644 --- a/packages/go_router/test/on_enter_test.dart +++ b/packages/go_router/test/on_enter_test.dart @@ -898,6 +898,60 @@ void main() { expect(find.text('Start'), findsOneWidget); }); + // Regression test for https://github.com/flutter/flutter/issues/179370. + // A SYNCHRONOUS onEnter that returns `Block.then(() => router.go(...))` + // on the initial route must still run the `then` callback so the + // navigation redirect occurs. Previously the sync path resolved the + // SynchronousFuture inline, causing the deferred microtask to run + // before the Router had finished the initial navigation, which broke + // the redirect that the user added the `async` keyword to work around. + testWidgets( + 'Synchronous onEnter Block.then(router.go) redirects from initial route', + (WidgetTester tester) async { + late GoRouter localRouter; + localRouter = GoRouter( + initialLocation: '/dashboard', + // Note: deliberately NOT marked `async`. + onEnter: + ( + BuildContext context, + GoRouterState current, + GoRouterState next, + GoRouter goRouter, + ) { + // Mirrors the exact predicate from #179370: the user keys + // off `current.uri.path`, which on the initial parse equals + // `nextState.uri.path` because the router has no committed + // configuration yet. + if (current.uri.path == '/dashboard') { + return Block.then(() => goRouter.go('/login')); + } + return const Allow(); + }, + routes: [ + GoRoute( + path: '/dashboard', + builder: (_, __) => + const Scaffold(body: Center(child: Text('Dashboard'))), + ), + GoRoute( + path: '/login', + builder: (_, __) => + const Scaffold(body: Center(child: Text('Login'))), + ), + ], + ); + router = localRouter; + + await tester.pumpWidget(MaterialApp.router(routerConfig: localRouter)); + await tester.pumpAndSettle(); + + expect(localRouter.state.uri.path, equals('/login')); + expect(find.text('Login'), findsOneWidget); + expect(find.text('Dashboard'), findsNothing); + }, + ); + testWidgets('Should call onException when exceptions thrown in onEnter callback', ( WidgetTester tester, ) async { From 327952e9f8bdd97c53bb2ffdb0f8aee57a50ec69 Mon Sep 17 00:00:00 2001 From: Nadeem Iqbal Date: Sun, 17 May 2026 23:20:33 +0500 Subject: [PATCH 2/2] [go_router] Address review: use group-level router instead of localRouter Address gemini-code-assist feedback on #11725: remove the redundant `localRouter` variable and use the existing group-level `router` directly, matching the pattern used in every other test in this file. --- packages/go_router/test/on_enter_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/go_router/test/on_enter_test.dart b/packages/go_router/test/on_enter_test.dart index 133dc708a32a..dec0b2fb2189 100644 --- a/packages/go_router/test/on_enter_test.dart +++ b/packages/go_router/test/on_enter_test.dart @@ -908,8 +908,7 @@ void main() { testWidgets( 'Synchronous onEnter Block.then(router.go) redirects from initial route', (WidgetTester tester) async { - late GoRouter localRouter; - localRouter = GoRouter( + router = GoRouter( initialLocation: '/dashboard', // Note: deliberately NOT marked `async`. onEnter: @@ -941,12 +940,11 @@ void main() { ), ], ); - router = localRouter; - await tester.pumpWidget(MaterialApp.router(routerConfig: localRouter)); + await tester.pumpWidget(MaterialApp.router(routerConfig: router)); await tester.pumpAndSettle(); - expect(localRouter.state.uri.path, equals('/login')); + expect(router.state.uri.path, equals('/login')); expect(find.text('Login'), findsOneWidget); expect(find.text('Dashboard'), findsNothing); },