From eb9388334046d17e4dfdb9309ab48938b863ff21 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:26:26 +0300 Subject: [PATCH 01/18] [go_router] Restore SDK app page adapters --- packages/go_router/lib/src/builder.dart | 32 +++-- .../go_router/lib/src/pages/app_type.dart | 50 +++++++ .../go_router/lib/src/pages/cupertino.dart | 77 ++++++++-- .../go_router/lib/src/pages/material.dart | 74 ++++++++-- .../sdk_app_compatibility.yaml | 3 + packages/go_router/test/sdk_app_test.dart | 133 ++++++++++++++++++ 6 files changed, 337 insertions(+), 32 deletions(-) create mode 100644 packages/go_router/lib/src/pages/app_type.dart create mode 100644 packages/go_router/pending_changelogs/sdk_app_compatibility.yaml create mode 100644 packages/go_router/test/sdk_app_test.dart diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart index e58662646681..969f1e71e179 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -9,6 +9,7 @@ import 'logging.dart'; import 'match.dart'; import 'misc/error_screen.dart'; import 'misc/errors.dart'; +import 'pages/app_type.dart'; import 'pages/cupertino.dart'; import 'pages/custom_transition_page.dart'; import 'pages/material.dart'; @@ -180,15 +181,13 @@ class _CustomNavigatorState extends State<_CustomNavigator> { void didChangeDependencies() { super.didChangeDependencies(); // Create a HeroController based on the app type. - if (_controller == null) { - if (isMaterialApp(context)) { - _controller = createMaterialHeroController(); - } else if (isCupertinoApp(context)) { - _controller = createCupertinoHeroController(); - } else { - _controller = HeroController(); - } - } + _controller ??= switch (appTypeOf(context)) { + AppType.sdkMaterial => createSdkMaterialHeroController(), + AppType.materialUi => createMaterialHeroController(), + AppType.sdkCupertino => createSdkCupertinoHeroController(), + AppType.cupertinoUi => createCupertinoHeroController(), + null => HeroController(), + }; // This method can also be called if any of the page builders depend on // the context. In this case, make sure _pages are rebuilt. _pages = null; @@ -347,12 +346,23 @@ class _CustomNavigatorState extends State<_CustomNavigator> { // can be null during testing final Element? elem = context is Element ? context : null; + final AppType? appType = elem == null ? null : appTypeOf(elem); - if (elem != null && isMaterialApp(elem)) { + if (appType == AppType.sdkMaterial) { + log('Using MaterialApp configuration'); + _pageBuilderForAppType = pageBuilderForSdkMaterialApp; + _errorBuilderForAppType = (BuildContext c, GoRouterState s) => + SdkMaterialErrorScreen(s.error); + } else if (appType == AppType.materialUi) { log('Using MaterialApp configuration'); _pageBuilderForAppType = pageBuilderForMaterialApp; _errorBuilderForAppType = (BuildContext c, GoRouterState s) => MaterialErrorScreen(s.error); - } else if (elem != null && isCupertinoApp(elem)) { + } else if (appType == AppType.sdkCupertino) { + log('Using CupertinoApp configuration'); + _pageBuilderForAppType = pageBuilderForSdkCupertinoApp; + _errorBuilderForAppType = (BuildContext c, GoRouterState s) => + SdkCupertinoErrorScreen(s.error); + } else if (appType == AppType.cupertinoUi) { log('Using CupertinoApp configuration'); _pageBuilderForAppType = pageBuilderForCupertinoApp; _errorBuilderForAppType = (BuildContext c, GoRouterState s) => diff --git a/packages/go_router/lib/src/pages/app_type.dart b/packages/go_router/lib/src/pages/app_type.dart new file mode 100644 index 000000000000..8bb04de32f9a --- /dev/null +++ b/packages/go_router/lib/src/pages/app_type.dart @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:cupertino_ui/cupertino_ui.dart' as cupertino_ui; +import 'package:flutter/cupertino.dart' as flutter_cupertino; +import 'package:flutter/material.dart' as flutter_material; +import 'package:flutter/widgets.dart'; +import 'package:material_ui/material_ui.dart' as material_ui; + +/// The app implementation that contains a go_router Navigator. +enum AppType { + /// A MaterialApp from the Flutter SDK. + sdkMaterial, + + /// A MaterialApp from material_ui. + materialUi, + + /// A CupertinoApp from the Flutter SDK. + sdkCupertino, + + /// A CupertinoApp from cupertino_ui. + cupertinoUi, +} + +/// Finds the closest supported app implementation in the widget tree. +AppType? appTypeOf(BuildContext context) { + AppType? result; + context.visitAncestorElements((Element element) { + final Widget widget = element.widget; + if (widget is flutter_material.MaterialApp) { + result = AppType.sdkMaterial; + return false; + } + if (widget is material_ui.MaterialApp) { + result = AppType.materialUi; + return false; + } + if (widget is flutter_cupertino.CupertinoApp) { + result = AppType.sdkCupertino; + return false; + } + if (widget is cupertino_ui.CupertinoApp) { + result = AppType.cupertinoUi; + return false; + } + return true; + }); + return result; +} diff --git a/packages/go_router/lib/src/pages/cupertino.dart b/packages/go_router/lib/src/pages/cupertino.dart index b31424262ab8..e3da1602e102 100644 --- a/packages/go_router/lib/src/pages/cupertino.dart +++ b/packages/go_router/lib/src/pages/cupertino.dart @@ -4,24 +4,48 @@ // ignore_for_file: diagnostic_describe_all_properties -import 'package:cupertino_ui/cupertino_ui.dart'; +import 'package:cupertino_ui/cupertino_ui.dart' as cupertino_ui; +import 'package:flutter/cupertino.dart' as flutter_cupertino; +import 'package:flutter/widgets.dart'; + import '../misc/extensions.dart'; /// Checks for CupertinoApp in the widget tree. bool isCupertinoApp(BuildContext context) => - context.findAncestorWidgetOfExactType() != null; + context.findAncestorWidgetOfExactType() != null || + context.findAncestorWidgetOfExactType() != null; + +/// Creates a Cupertino HeroController from cupertino_ui. +HeroController createCupertinoHeroController() => + cupertino_ui.CupertinoApp.createCupertinoHeroController(); -/// Creates a Cupertino HeroController. -HeroController createCupertinoHeroController() => CupertinoApp.createCupertinoHeroController(); +/// Creates a Cupertino HeroController from the Flutter SDK. +HeroController createSdkCupertinoHeroController() => + flutter_cupertino.CupertinoApp.createCupertinoHeroController(); -/// Builds a Cupertino page. -CupertinoPage pageBuilderForCupertinoApp({ +/// Builds a Cupertino page from cupertino_ui. +cupertino_ui.CupertinoPage pageBuilderForCupertinoApp({ required LocalKey key, required String? name, required Object? arguments, required String restorationId, required Widget child, -}) => CupertinoPage( +}) => cupertino_ui.CupertinoPage( + name: name, + arguments: arguments, + key: key, + restorationId: restorationId, + child: child, +); + +/// Builds a Cupertino page from the Flutter SDK. +flutter_cupertino.CupertinoPage pageBuilderForSdkCupertinoApp({ + required LocalKey key, + required String? name, + required Object? arguments, + required String restorationId, + required Widget child, +}) => flutter_cupertino.CupertinoPage( name: name, arguments: arguments, key: key, @@ -38,14 +62,47 @@ class CupertinoErrorScreen extends StatelessWidget { final Exception? error; @override - Widget build(BuildContext context) => CupertinoPageScaffold( - navigationBar: const CupertinoNavigationBar(middle: Text('Page Not Found')), + Widget build(BuildContext context) => cupertino_ui.CupertinoPageScaffold( + navigationBar: const cupertino_ui.CupertinoNavigationBar( + middle: Text('Page Not Found'), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(error?.toString() ?? 'page not found'), + cupertino_ui.CupertinoButton( + onPressed: () => context.go('/'), + child: const Text('Home'), + ), + ], + ), + ), + ); +} + +/// Default error page implementation for an SDK CupertinoApp. +class SdkCupertinoErrorScreen extends StatelessWidget { + /// Provide an exception to this page for it to be displayed. + const SdkCupertinoErrorScreen(this.error, {super.key}); + + /// The exception to be displayed. + final Exception? error; + + @override + Widget build(BuildContext context) => flutter_cupertino.CupertinoPageScaffold( + navigationBar: const flutter_cupertino.CupertinoNavigationBar( + middle: Text('Page Not Found'), + ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(error?.toString() ?? 'page not found'), - CupertinoButton(onPressed: () => context.go('/'), child: const Text('Home')), + flutter_cupertino.CupertinoButton( + onPressed: () => context.go('/'), + child: const Text('Home'), + ), ], ), ), diff --git a/packages/go_router/lib/src/pages/material.dart b/packages/go_router/lib/src/pages/material.dart index e5f28b667919..92ebac3cdc3a 100644 --- a/packages/go_router/lib/src/pages/material.dart +++ b/packages/go_router/lib/src/pages/material.dart @@ -4,25 +4,48 @@ // ignore_for_file: diagnostic_describe_all_properties -import 'package:material_ui/material_ui.dart'; +import 'package:flutter/material.dart' as flutter_material; +import 'package:flutter/widgets.dart'; +import 'package:material_ui/material_ui.dart' as material_ui; import '../misc/extensions.dart'; /// Checks for MaterialApp in the widget tree. bool isMaterialApp(BuildContext context) => - context.findAncestorWidgetOfExactType() != null; + context.findAncestorWidgetOfExactType() != null || + context.findAncestorWidgetOfExactType() != null; -/// Creates a Material HeroController. -HeroController createMaterialHeroController() => MaterialApp.createMaterialHeroController(); +/// Creates a Material HeroController from material_ui. +HeroController createMaterialHeroController() => + material_ui.MaterialApp.createMaterialHeroController(); -/// Builds a Material page. -MaterialPage pageBuilderForMaterialApp({ +/// Creates a Material HeroController from the Flutter SDK. +HeroController createSdkMaterialHeroController() => + flutter_material.MaterialApp.createMaterialHeroController(); + +/// Builds a Material page from material_ui. +material_ui.MaterialPage pageBuilderForMaterialApp({ required LocalKey key, required String? name, required Object? arguments, required String restorationId, required Widget child, -}) => MaterialPage( +}) => material_ui.MaterialPage( + name: name, + arguments: arguments, + key: key, + restorationId: restorationId, + child: child, +); + +/// Builds a Material page from the Flutter SDK. +flutter_material.MaterialPage pageBuilderForSdkMaterialApp({ + required LocalKey key, + required String? name, + required Object? arguments, + required String restorationId, + required Widget child, +}) => flutter_material.MaterialPage( name: name, arguments: arguments, key: key, @@ -39,14 +62,43 @@ class MaterialErrorScreen extends StatelessWidget { final Exception? error; @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text('Page Not Found')), + Widget build(BuildContext context) => material_ui.Scaffold( + appBar: material_ui.AppBar(title: const Text('Page Not Found')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + material_ui.SelectableText(error?.toString() ?? 'page not found'), + material_ui.TextButton( + onPressed: () => context.go('/'), + child: const Text('Home'), + ), + ], + ), + ), + ); +} + +/// Default error page implementation for an SDK MaterialApp. +class SdkMaterialErrorScreen extends StatelessWidget { + /// Provide an exception to this page for it to be displayed. + const SdkMaterialErrorScreen(this.error, {super.key}); + + /// The exception to be displayed. + final Exception? error; + + @override + Widget build(BuildContext context) => flutter_material.Scaffold( + appBar: flutter_material.AppBar(title: const Text('Page Not Found')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SelectableText(error?.toString() ?? 'page not found'), - TextButton(onPressed: () => context.go('/'), child: const Text('Home')), + flutter_material.SelectableText(error?.toString() ?? 'page not found'), + flutter_material.TextButton( + onPressed: () => context.go('/'), + child: const Text('Home'), + ), ], ), ), diff --git a/packages/go_router/pending_changelogs/sdk_app_compatibility.yaml b/packages/go_router/pending_changelogs/sdk_app_compatibility.yaml new file mode 100644 index 000000000000..ab3e3d79d3ef --- /dev/null +++ b/packages/go_router/pending_changelogs/sdk_app_compatibility.yaml @@ -0,0 +1,3 @@ +changelog: | + - Restores SDK MaterialApp and CupertinoApp support when selecting default pages, hero controllers, and error screens, while preserving material_ui and cupertino_ui support. +version: patch diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart new file mode 100644 index 000000000000..4d78064c72a6 --- /dev/null +++ b/packages/go_router/test/sdk_app_test.dart @@ -0,0 +1,133 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:cupertino_ui/cupertino_ui.dart' as cupertino_ui; +import 'package:flutter/cupertino.dart' as flutter_cupertino; +import 'package:flutter/material.dart' as flutter_material; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; +import 'package:material_ui/material_ui.dart' as material_ui; + +void main() { + testWidgets('GoRoute.builder uses SDK Material configuration for SDK MaterialApp', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => flutter_material.MaterialApp.router(routerConfig: router), + ); + + expect(result.settings, isA>()); + final controller = result.heroController!; + final tween = controller.createRectTween!( + const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), + const Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), + ); + expect(tween, isA()); + }); + + testWidgets('GoRoute.builder keeps material_ui configuration for material_ui MaterialApp', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => material_ui.MaterialApp.router(routerConfig: router), + ); + + expect(result.settings, isA>()); + final controller = result.heroController!; + final tween = controller.createRectTween!( + const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), + const Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), + ); + expect(tween, isA()); + }); + + testWidgets('GoRoute.builder uses SDK CupertinoPage for SDK CupertinoApp', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => flutter_cupertino.CupertinoApp.router(routerConfig: router), + ); + + expect(result.settings, isA>()); + }); + + testWidgets('GoRoute.builder keeps cupertino_ui CupertinoPage for cupertino_ui CupertinoApp', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => cupertino_ui.CupertinoApp.router(routerConfig: router), + ); + + expect(result.settings, isA>()); + }); + + testWidgets('SDK MaterialApp uses the SDK Material error screen', (WidgetTester tester) async { + final router = _errorRouter(); + addTearDown(router.dispose); + + await tester.pumpWidget(flutter_material.MaterialApp.router(routerConfig: router)); + await tester.pumpAndSettle(); + + expect(find.byType(flutter_material.Scaffold), findsOneWidget); + expect(find.byType(material_ui.Scaffold), findsNothing); + }); + + testWidgets('SDK CupertinoApp uses the SDK Cupertino error screen', (WidgetTester tester) async { + final router = _errorRouter(); + addTearDown(router.dispose); + + await tester.pumpWidget(flutter_cupertino.CupertinoApp.router(routerConfig: router)); + await tester.pumpAndSettle(); + + expect(find.byType(flutter_cupertino.CupertinoPageScaffold), findsOneWidget); + expect(find.byType(cupertino_ui.CupertinoPageScaffold), findsNothing); + }); +} + +typedef _AppBuilder = Widget Function(GoRouter router); + +Future<({RouteSettings? settings, HeroController? heroController})> _pumpApp( + WidgetTester tester, + _AppBuilder appBuilder, +) async { + RouteSettings? settings; + HeroController? heroController; + final router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (BuildContext context, GoRouterState state) => Builder( + builder: (BuildContext context) { + settings = ModalRoute.of(context)?.settings; + heroController = HeroControllerScope.maybeOf(context); + return const SizedBox.shrink(); + }, + ), + ), + ], + ); + addTearDown(router.dispose); + + await tester.pumpWidget(appBuilder(router)); + await tester.pumpAndSettle(); + + return (settings: settings, heroController: heroController); +} + +GoRouter _errorRouter() => GoRouter( + initialLocation: '/missing', + routes: [ + GoRoute( + path: '/', + builder: (BuildContext context, GoRouterState state) => const SizedBox.shrink(), + ), + ], +); From 42e7aa8f60f5ec030c09f45bc655b82e2af75d28 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:22:05 +0300 Subject: [PATCH 02/18] Expand SDK app compatibility regression coverage --- packages/go_router/test/sdk_app_test.dart | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart index 4d78064c72a6..4fc78a94cccf 100644 --- a/packages/go_router/test/sdk_app_test.dart +++ b/packages/go_router/test/sdk_app_test.dart @@ -69,6 +69,32 @@ void main() { expect(result.settings, isA>()); }); + testWidgets('GoRoute.builder uses the closest supported app when Cupertino is nested in Material', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => flutter_material.MaterialApp( + home: flutter_cupertino.CupertinoApp.router(routerConfig: router), + ), + ); + + expect(result.settings, isA>()); + }); + + testWidgets('GoRoute.builder uses the closest supported app when Material is nested in Cupertino', ( + WidgetTester tester, + ) async { + final result = await _pumpApp( + tester, + (GoRouter router) => flutter_cupertino.CupertinoApp( + home: flutter_material.MaterialApp.router(routerConfig: router), + ), + ); + + expect(result.settings, isA>()); + }); + testWidgets('SDK MaterialApp uses the SDK Material error screen', (WidgetTester tester) async { final router = _errorRouter(); addTearDown(router.dispose); @@ -80,6 +106,19 @@ void main() { expect(find.byType(material_ui.Scaffold), findsNothing); }); + testWidgets('material_ui MaterialApp keeps the material_ui error screen', ( + WidgetTester tester, + ) async { + final router = _errorRouter(); + addTearDown(router.dispose); + + await tester.pumpWidget(material_ui.MaterialApp.router(routerConfig: router)); + await tester.pumpAndSettle(); + + expect(find.byType(material_ui.Scaffold), findsOneWidget); + expect(find.byType(flutter_material.Scaffold), findsNothing); + }); + testWidgets('SDK CupertinoApp uses the SDK Cupertino error screen', (WidgetTester tester) async { final router = _errorRouter(); addTearDown(router.dispose); @@ -90,6 +129,19 @@ void main() { expect(find.byType(flutter_cupertino.CupertinoPageScaffold), findsOneWidget); expect(find.byType(cupertino_ui.CupertinoPageScaffold), findsNothing); }); + + testWidgets('cupertino_ui CupertinoApp keeps the cupertino_ui error screen', ( + WidgetTester tester, + ) async { + final router = _errorRouter(); + addTearDown(router.dispose); + + await tester.pumpWidget(cupertino_ui.CupertinoApp.router(routerConfig: router)); + await tester.pumpAndSettle(); + + expect(find.byType(cupertino_ui.CupertinoPageScaffold), findsOneWidget); + expect(find.byType(flutter_cupertino.CupertinoPageScaffold), findsNothing); + }); } typedef _AppBuilder = Widget Function(GoRouter router); From 77f7ca1962f6f5fc902d005a950452c1a8b366d7 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:38:28 +0300 Subject: [PATCH 03/18] ci: temporarily validate PR 12924 regression --- .github/workflows/pr12924-validation.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/pr12924-validation.yml diff --git a/.github/workflows/pr12924-validation.yml b/.github/workflows/pr12924-validation.yml new file mode 100644 index 000000000000..00c5a14c552b --- /dev/null +++ b/.github/workflows/pr12924-validation.yml @@ -0,0 +1,19 @@ +name: PR 12924 targeted validation + +on: + push: + branches: + - fix-go-router-sdk-app-pages + +jobs: + go-router-regression: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2.21.0 + with: + channel: master + - run: flutter pub get + - name: Run targeted go_router regression tests + working-directory: packages/go_router + run: flutter test test/sdk_app_test.dart From 386085d299595a7f6828a5b68afb52d7e6090357 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:40:28 +0300 Subject: [PATCH 04/18] ci: rerun PR 12924 validation from package root --- .github/workflows/pr12924-validation.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr12924-validation.yml b/.github/workflows/pr12924-validation.yml index 00c5a14c552b..9e3ed937c69e 100644 --- a/.github/workflows/pr12924-validation.yml +++ b/.github/workflows/pr12924-validation.yml @@ -5,6 +5,9 @@ on: branches: - fix-go-router-sdk-app-pages +permissions: + contents: write + jobs: go-router-regression: runs-on: ubuntu-latest @@ -13,7 +16,16 @@ jobs: - uses: subosito/flutter-action@v2.21.0 with: channel: master - - run: flutter pub get + - name: Resolve go_router dependencies + working-directory: packages/go_router + run: flutter pub get - name: Run targeted go_router regression tests working-directory: packages/go_router run: flutter test test/sdk_app_test.dart + - name: Remove temporary validation workflow + run: | + git config user.name "Yazan Arafeh" + git config user.email "32390922+yazanmg@users.noreply.github.com" + git rm .github/workflows/pr12924-validation.yml + git commit -m "ci: remove temporary PR 12924 validation" + git push origin HEAD:fix-go-router-sdk-app-pages From 6d8fbd9073ed9c19704f57bc721f2a43fd6c1d1b Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:43:01 +0300 Subject: [PATCH 05/18] Fix material hero controller regression coverage --- packages/go_router/test/sdk_app_test.dart | 57 +++++++++++------------ 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart index 4fc78a94cccf..d632e796c788 100644 --- a/packages/go_router/test/sdk_app_test.dart +++ b/packages/go_router/test/sdk_app_test.dart @@ -10,89 +10,89 @@ import 'package:flutter/material.dart' as flutter_material; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; +import 'package:go_router/src/pages/material.dart' as go_material; import 'package:material_ui/material_ui.dart' as material_ui; void main() { + test('Material hero controller factories keep their matching implementations', () { + const begin = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); + const end = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); + + final sdkTween = go_material.createSdkMaterialHeroController().createRectTween!(begin, end); + expect(sdkTween, isA()); + + final materialUiTween = go_material.createMaterialHeroController().createRectTween!(begin, end); + expect(materialUiTween, isA()); + }); + testWidgets('GoRoute.builder uses SDK Material configuration for SDK MaterialApp', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => flutter_material.MaterialApp.router(routerConfig: router), ); - expect(result.settings, isA>()); - final controller = result.heroController!; - final tween = controller.createRectTween!( - const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), - const Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), - ); - expect(tween, isA()); + expect(settings, isA>()); }); testWidgets('GoRoute.builder keeps material_ui configuration for material_ui MaterialApp', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => material_ui.MaterialApp.router(routerConfig: router), ); - expect(result.settings, isA>()); - final controller = result.heroController!; - final tween = controller.createRectTween!( - const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), - const Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), - ); - expect(tween, isA()); + expect(settings, isA>()); }); testWidgets('GoRoute.builder uses SDK CupertinoPage for SDK CupertinoApp', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => flutter_cupertino.CupertinoApp.router(routerConfig: router), ); - expect(result.settings, isA>()); + expect(settings, isA>()); }); testWidgets('GoRoute.builder keeps cupertino_ui CupertinoPage for cupertino_ui CupertinoApp', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => cupertino_ui.CupertinoApp.router(routerConfig: router), ); - expect(result.settings, isA>()); + expect(settings, isA>()); }); testWidgets('GoRoute.builder uses the closest supported app when Cupertino is nested in Material', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => flutter_material.MaterialApp( home: flutter_cupertino.CupertinoApp.router(routerConfig: router), ), ); - expect(result.settings, isA>()); + expect(settings, isA>()); }); testWidgets('GoRoute.builder uses the closest supported app when Material is nested in Cupertino', ( WidgetTester tester, ) async { - final result = await _pumpApp( + final settings = await _pumpApp( tester, (GoRouter router) => flutter_cupertino.CupertinoApp( home: flutter_material.MaterialApp.router(routerConfig: router), ), ); - expect(result.settings, isA>()); + expect(settings, isA>()); }); testWidgets('SDK MaterialApp uses the SDK Material error screen', (WidgetTester tester) async { @@ -146,12 +146,8 @@ void main() { typedef _AppBuilder = Widget Function(GoRouter router); -Future<({RouteSettings? settings, HeroController? heroController})> _pumpApp( - WidgetTester tester, - _AppBuilder appBuilder, -) async { +Future _pumpApp(WidgetTester tester, _AppBuilder appBuilder) async { RouteSettings? settings; - HeroController? heroController; final router = GoRouter( routes: [ GoRoute( @@ -159,7 +155,6 @@ Future<({RouteSettings? settings, HeroController? heroController})> _pumpApp( builder: (BuildContext context, GoRouterState state) => Builder( builder: (BuildContext context) { settings = ModalRoute.of(context)?.settings; - heroController = HeroControllerScope.maybeOf(context); return const SizedBox.shrink(); }, ), @@ -171,7 +166,7 @@ Future<({RouteSettings? settings, HeroController? heroController})> _pumpApp( await tester.pumpWidget(appBuilder(router)); await tester.pumpAndSettle(); - return (settings: settings, heroController: heroController); + return settings; } GoRouter _errorRouter() => GoRouter( From 635136a1c94ede31587a812a5709f3b9a0cb5a4e Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:44:46 +0000 Subject: [PATCH 06/18] ci: remove temporary PR 12924 validation --- .github/workflows/pr12924-validation.yml | 31 ------------------------ 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/pr12924-validation.yml diff --git a/.github/workflows/pr12924-validation.yml b/.github/workflows/pr12924-validation.yml deleted file mode 100644 index 9e3ed937c69e..000000000000 --- a/.github/workflows/pr12924-validation.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: PR 12924 targeted validation - -on: - push: - branches: - - fix-go-router-sdk-app-pages - -permissions: - contents: write - -jobs: - go-router-regression: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.21.0 - with: - channel: master - - name: Resolve go_router dependencies - working-directory: packages/go_router - run: flutter pub get - - name: Run targeted go_router regression tests - working-directory: packages/go_router - run: flutter test test/sdk_app_test.dart - - name: Remove temporary validation workflow - run: | - git config user.name "Yazan Arafeh" - git config user.email "32390922+yazanmg@users.noreply.github.com" - git rm .github/workflows/pr12924-validation.yml - git commit -m "ci: remove temporary PR 12924 validation" - git push origin HEAD:fix-go-router-sdk-app-pages From c84c841187f07b2dc4b93c4d0f5703adaa96ef56 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:45:30 +0300 Subject: [PATCH 07/18] ci: format and revalidate PR 12924 --- .github/workflows/pr12924-format.yml | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/pr12924-format.yml diff --git a/.github/workflows/pr12924-format.yml b/.github/workflows/pr12924-format.yml new file mode 100644 index 000000000000..b7536d858bcc --- /dev/null +++ b/.github/workflows/pr12924-format.yml @@ -0,0 +1,40 @@ +name: PR 12924 format validation + +on: + push: + branches: + - fix-go-router-sdk-app-pages + +permissions: + contents: write + +jobs: + format-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2.21.0 + with: + channel: master + - name: Format changed Dart files + run: | + dart format \ + packages/go_router/lib/src/builder.dart \ + packages/go_router/lib/src/pages/app_type.dart \ + packages/go_router/lib/src/pages/cupertino.dart \ + packages/go_router/lib/src/pages/material.dart \ + packages/go_router/test/sdk_app_test.dart + - name: Resolve go_router dependencies + working-directory: packages/go_router + run: flutter pub get + - name: Run targeted go_router regression tests + working-directory: packages/go_router + run: flutter test test/sdk_app_test.dart + - name: Commit formatting and remove temporary workflow + run: | + git config user.name "Yazan Arafeh" + git config user.email "32390922+yazanmg@users.noreply.github.com" + git rm .github/workflows/pr12924-format.yml + git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart + git commit -m "Format go_router SDK app compatibility changes" + git push origin HEAD:fix-go-router-sdk-app-pages From 8695da6e67e89c8dd338b963363db0b0bf8d2f53 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:47:19 +0000 Subject: [PATCH 08/18] Format go_router SDK app compatibility changes --- .github/workflows/pr12924-format.yml | 40 ---------------- packages/go_router/lib/src/builder.dart | 22 +++++---- .../go_router/lib/src/pages/cupertino.dart | 13 ++---- .../go_router/lib/src/pages/material.dart | 10 +--- packages/go_router/test/sdk_app_test.dart | 46 ++++++++++--------- 5 files changed, 41 insertions(+), 90 deletions(-) delete mode 100644 .github/workflows/pr12924-format.yml diff --git a/.github/workflows/pr12924-format.yml b/.github/workflows/pr12924-format.yml deleted file mode 100644 index b7536d858bcc..000000000000 --- a/.github/workflows/pr12924-format.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: PR 12924 format validation - -on: - push: - branches: - - fix-go-router-sdk-app-pages - -permissions: - contents: write - -jobs: - format-and-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2.21.0 - with: - channel: master - - name: Format changed Dart files - run: | - dart format \ - packages/go_router/lib/src/builder.dart \ - packages/go_router/lib/src/pages/app_type.dart \ - packages/go_router/lib/src/pages/cupertino.dart \ - packages/go_router/lib/src/pages/material.dart \ - packages/go_router/test/sdk_app_test.dart - - name: Resolve go_router dependencies - working-directory: packages/go_router - run: flutter pub get - - name: Run targeted go_router regression tests - working-directory: packages/go_router - run: flutter test test/sdk_app_test.dart - - name: Commit formatting and remove temporary workflow - run: | - git config user.name "Yazan Arafeh" - git config user.email "32390922+yazanmg@users.noreply.github.com" - git rm .github/workflows/pr12924-format.yml - git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - git commit -m "Format go_router SDK app compatibility changes" - git push origin HEAD:fix-go-router-sdk-app-pages diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart index 969f1e71e179..69350b6dc5d8 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -20,14 +20,13 @@ import 'state.dart'; /// Signature of a go router builder function with navigator. typedef GoRouterBuilderWithNav = Widget Function(BuildContext context, Widget child); -typedef _PageBuilderForAppType = - Page Function({ - required LocalKey key, - required String? name, - required Object? arguments, - required String restorationId, - required Widget child, - }); +typedef _PageBuilderForAppType = Page Function({ + required LocalKey key, + required String? name, + required Object? arguments, + required String restorationId, + required Widget child, +}); typedef _ErrorBuilderForAppType = Widget Function(BuildContext context, GoRouterState state); @@ -39,8 +38,11 @@ typedef _ErrorBuilderForAppType = Widget Function(BuildContext context, GoRouter /// associates with. /// /// Used by of [RouteBuilder.onPopPageWithRouteMatch]. -typedef PopPageWithRouteMatchCallback = - bool Function(Route route, dynamic result, RouteMatchBase match); +typedef PopPageWithRouteMatchCallback = bool Function( + Route route, + dynamic result, + RouteMatchBase match, +); /// Builds the top-level Navigator for GoRouter. class RouteBuilder { diff --git a/packages/go_router/lib/src/pages/cupertino.dart b/packages/go_router/lib/src/pages/cupertino.dart index e3da1602e102..50a9dc9c71c1 100644 --- a/packages/go_router/lib/src/pages/cupertino.dart +++ b/packages/go_router/lib/src/pages/cupertino.dart @@ -63,18 +63,13 @@ class CupertinoErrorScreen extends StatelessWidget { @override Widget build(BuildContext context) => cupertino_ui.CupertinoPageScaffold( - navigationBar: const cupertino_ui.CupertinoNavigationBar( - middle: Text('Page Not Found'), - ), + navigationBar: const cupertino_ui.CupertinoNavigationBar(middle: Text('Page Not Found')), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(error?.toString() ?? 'page not found'), - cupertino_ui.CupertinoButton( - onPressed: () => context.go('/'), - child: const Text('Home'), - ), + cupertino_ui.CupertinoButton(onPressed: () => context.go('/'), child: const Text('Home')), ], ), ), @@ -91,9 +86,7 @@ class SdkCupertinoErrorScreen extends StatelessWidget { @override Widget build(BuildContext context) => flutter_cupertino.CupertinoPageScaffold( - navigationBar: const flutter_cupertino.CupertinoNavigationBar( - middle: Text('Page Not Found'), - ), + navigationBar: const flutter_cupertino.CupertinoNavigationBar(middle: Text('Page Not Found')), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/packages/go_router/lib/src/pages/material.dart b/packages/go_router/lib/src/pages/material.dart index 92ebac3cdc3a..c98dc413258e 100644 --- a/packages/go_router/lib/src/pages/material.dart +++ b/packages/go_router/lib/src/pages/material.dart @@ -69,10 +69,7 @@ class MaterialErrorScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ material_ui.SelectableText(error?.toString() ?? 'page not found'), - material_ui.TextButton( - onPressed: () => context.go('/'), - child: const Text('Home'), - ), + material_ui.TextButton(onPressed: () => context.go('/'), child: const Text('Home')), ], ), ), @@ -95,10 +92,7 @@ class SdkMaterialErrorScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ flutter_material.SelectableText(error?.toString() ?? 'page not found'), - flutter_material.TextButton( - onPressed: () => context.go('/'), - child: const Text('Home'), - ), + flutter_material.TextButton(onPressed: () => context.go('/'), child: const Text('Home')), ], ), ), diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart index d632e796c788..5cad7a7e8748 100644 --- a/packages/go_router/test/sdk_app_test.dart +++ b/packages/go_router/test/sdk_app_test.dart @@ -69,31 +69,33 @@ void main() { expect(settings, isA>()); }); - testWidgets('GoRoute.builder uses the closest supported app when Cupertino is nested in Material', ( - WidgetTester tester, - ) async { - final settings = await _pumpApp( - tester, - (GoRouter router) => flutter_material.MaterialApp( - home: flutter_cupertino.CupertinoApp.router(routerConfig: router), - ), - ); + testWidgets( + 'GoRoute.builder uses the closest supported app when Cupertino is nested in Material', + (WidgetTester tester) async { + final settings = await _pumpApp( + tester, + (GoRouter router) => flutter_material.MaterialApp( + home: flutter_cupertino.CupertinoApp.router(routerConfig: router), + ), + ); - expect(settings, isA>()); - }); + expect(settings, isA>()); + }, + ); - testWidgets('GoRoute.builder uses the closest supported app when Material is nested in Cupertino', ( - WidgetTester tester, - ) async { - final settings = await _pumpApp( - tester, - (GoRouter router) => flutter_cupertino.CupertinoApp( - home: flutter_material.MaterialApp.router(routerConfig: router), - ), - ); + testWidgets( + 'GoRoute.builder uses the closest supported app when Material is nested in Cupertino', + (WidgetTester tester) async { + final settings = await _pumpApp( + tester, + (GoRouter router) => flutter_cupertino.CupertinoApp( + home: flutter_material.MaterialApp.router(routerConfig: router), + ), + ); - expect(settings, isA>()); - }); + expect(settings, isA>()); + }, + ); testWidgets('SDK MaterialApp uses the SDK Material error screen', (WidgetTester tester) async { final router = _errorRouter(); From b17a01d5c99217797c06c9f20e3e5201a7784a5e Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:00:11 +0300 Subject: [PATCH 09/18] ci: run full go_router validation --- .github/workflows/pr12924-full-package.yml | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/pr12924-full-package.yml diff --git a/.github/workflows/pr12924-full-package.yml b/.github/workflows/pr12924-full-package.yml new file mode 100644 index 000000000000..9f64c89e9982 --- /dev/null +++ b/.github/workflows/pr12924-full-package.yml @@ -0,0 +1,34 @@ +name: PR 12924 full go_router validation + +on: + push: + branches: + - fix-go-router-sdk-app-pages + +permissions: + contents: write + +jobs: + full-go-router-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: master + cache: true + - name: Resolve go_router dependencies + working-directory: packages/go_router + run: flutter pub get + - name: Format check changed Dart files + run: dart format --output=none --set-exit-if-changed packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart + - name: Run full go_router test suite + working-directory: packages/go_router + run: flutter test + - name: Remove temporary validation workflow + run: | + git config user.name "Yazan Arafeh" + git config user.email "32390922+yazanmg@users.noreply.github.com" + git rm .github/workflows/pr12924-full-package.yml + git commit -m "ci: remove temporary PR 12924 full validation" + git push origin HEAD:fix-go-router-sdk-app-pages From a9625d7da33faee65849182f0c05b9ccab56d49e Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:01:58 +0300 Subject: [PATCH 10/18] ci: format before full go_router validation --- .github/workflows/pr12924-full-package.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr12924-full-package.yml b/.github/workflows/pr12924-full-package.yml index 9f64c89e9982..966c6a385b3e 100644 --- a/.github/workflows/pr12924-full-package.yml +++ b/.github/workflows/pr12924-full-package.yml @@ -20,15 +20,16 @@ jobs: - name: Resolve go_router dependencies working-directory: packages/go_router run: flutter pub get - - name: Format check changed Dart files - run: dart format --output=none --set-exit-if-changed packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart + - name: Format changed Dart files + run: dart format packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - name: Run full go_router test suite working-directory: packages/go_router run: flutter test - - name: Remove temporary validation workflow + - name: Commit formatting and remove temporary validation workflow run: | git config user.name "Yazan Arafeh" git config user.email "32390922+yazanmg@users.noreply.github.com" + git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart git rm .github/workflows/pr12924-full-package.yml - git commit -m "ci: remove temporary PR 12924 full validation" + git commit -m "Format and validate go_router SDK app fix" git push origin HEAD:fix-go-router-sdk-app-pages From 7c109f4a02c9a41b340952deebe506037d4f142d Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:04:18 +0000 Subject: [PATCH 11/18] Format and validate go_router SDK app fix --- .github/workflows/pr12924-full-package.yml | 35 ---------------------- packages/go_router/lib/src/builder.dart | 22 +++++++------- 2 files changed, 10 insertions(+), 47 deletions(-) delete mode 100644 .github/workflows/pr12924-full-package.yml diff --git a/.github/workflows/pr12924-full-package.yml b/.github/workflows/pr12924-full-package.yml deleted file mode 100644 index 966c6a385b3e..000000000000 --- a/.github/workflows/pr12924-full-package.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: PR 12924 full go_router validation - -on: - push: - branches: - - fix-go-router-sdk-app-pages - -permissions: - contents: write - -jobs: - full-go-router-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - channel: master - cache: true - - name: Resolve go_router dependencies - working-directory: packages/go_router - run: flutter pub get - - name: Format changed Dart files - run: dart format packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - - name: Run full go_router test suite - working-directory: packages/go_router - run: flutter test - - name: Commit formatting and remove temporary validation workflow - run: | - git config user.name "Yazan Arafeh" - git config user.email "32390922+yazanmg@users.noreply.github.com" - git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - git rm .github/workflows/pr12924-full-package.yml - git commit -m "Format and validate go_router SDK app fix" - git push origin HEAD:fix-go-router-sdk-app-pages diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart index 69350b6dc5d8..969f1e71e179 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -20,13 +20,14 @@ import 'state.dart'; /// Signature of a go router builder function with navigator. typedef GoRouterBuilderWithNav = Widget Function(BuildContext context, Widget child); -typedef _PageBuilderForAppType = Page Function({ - required LocalKey key, - required String? name, - required Object? arguments, - required String restorationId, - required Widget child, -}); +typedef _PageBuilderForAppType = + Page Function({ + required LocalKey key, + required String? name, + required Object? arguments, + required String restorationId, + required Widget child, + }); typedef _ErrorBuilderForAppType = Widget Function(BuildContext context, GoRouterState state); @@ -38,11 +39,8 @@ typedef _ErrorBuilderForAppType = Widget Function(BuildContext context, GoRouter /// associates with. /// /// Used by of [RouteBuilder.onPopPageWithRouteMatch]. -typedef PopPageWithRouteMatchCallback = bool Function( - Route route, - dynamic result, - RouteMatchBase match, -); +typedef PopPageWithRouteMatchCallback = + bool Function(Route route, dynamic result, RouteMatchBase match); /// Builds the top-level Navigator for GoRouter. class RouteBuilder { From 0ba145a485836b16d91a54d0a96a82041b8a649a Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:29:48 +0300 Subject: [PATCH 12/18] ci: apply PR 12924 review fix --- .github/workflows/pr12924-review-fixes.yml | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/pr12924-review-fixes.yml diff --git a/.github/workflows/pr12924-review-fixes.yml b/.github/workflows/pr12924-review-fixes.yml new file mode 100644 index 000000000000..fdd20dad4020 --- /dev/null +++ b/.github/workflows/pr12924-review-fixes.yml @@ -0,0 +1,54 @@ +name: PR 12924 review fixes + +on: + push: + branches: + - fix-go-router-sdk-app-pages + +permissions: + contents: write + +jobs: + apply-and-validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: master + cache: true + - name: Resolve go_router dependencies + working-directory: packages/go_router + run: flutter pub get + - name: Apply cache invalidation fix + run: | + python3 - <<'PY' + from pathlib import Path + p = Path('packages/go_router/lib/src/builder.dart') + s = p.read_text() + old = ''' // This method can also be called if any of the page builders depend on + // the context. In this case, make sure _pages are rebuilt. + _pages = null;''' + new = ''' // This method can also be called if any of the page builders depend on + // the context. In this case, make sure the app-type builders and pages are + // recomputed from the current ancestor tree. + _pageBuilderForAppType = null; + _errorBuilderForAppType = null; + _pages = null;''' + if old not in s: + raise SystemExit('didChangeDependencies block not found') + p.write_text(s.replace(old, new, 1)) + PY + - name: Format changed Dart files + run: dart format packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart + - name: Run full go_router test suite + working-directory: packages/go_router + run: flutter test + - name: Commit fix and remove temporary workflow + run: | + git config user.name "Yazan Arafeh" + git config user.email "32390922+yazanmg@users.noreply.github.com" + git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart + git rm .github/workflows/pr12924-review-fixes.yml + git commit -m "Invalidate go_router app-type caches on dependency changes" + git push origin HEAD:fix-go-router-sdk-app-pages From 1a8cf0f8c5410a20976f502c741ed3c7c7951c2d Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:31:06 +0300 Subject: [PATCH 13/18] ci: fix PR 12924 review validation workflow --- .github/workflows/pr12924-review-fixes.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr12924-review-fixes.yml b/.github/workflows/pr12924-review-fixes.yml index fdd20dad4020..92c531ed9f54 100644 --- a/.github/workflows/pr12924-review-fixes.yml +++ b/.github/workflows/pr12924-review-fixes.yml @@ -26,15 +26,15 @@ jobs: from pathlib import Path p = Path('packages/go_router/lib/src/builder.dart') s = p.read_text() - old = ''' // This method can also be called if any of the page builders depend on - // the context. In this case, make sure _pages are rebuilt. - _pages = null;''' - new = ''' // This method can also be called if any of the page builders depend on - // the context. In this case, make sure the app-type builders and pages are - // recomputed from the current ancestor tree. - _pageBuilderForAppType = null; - _errorBuilderForAppType = null; - _pages = null;''' + old = (' // This method can also be called if any of the page builders depend on\n' + ' // the context. In this case, make sure _pages are rebuilt.\n' + ' _pages = null;') + new = (' // This method can also be called if any of the page builders depend on\n' + ' // the context. In this case, make sure the app-type builders and pages are\n' + ' // recomputed from the current ancestor tree.\n' + ' _pageBuilderForAppType = null;\n' + ' _errorBuilderForAppType = null;\n' + ' _pages = null;') if old not in s: raise SystemExit('didChangeDependencies block not found') p.write_text(s.replace(old, new, 1)) From 5dd166788f9374949f12725eee0a5aac50d096fd Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:32:25 +0000 Subject: [PATCH 14/18] Invalidate go_router app-type caches on dependency changes --- .github/workflows/pr12924-review-fixes.yml | 54 ---------------------- packages/go_router/lib/src/builder.dart | 5 +- 2 files changed, 4 insertions(+), 55 deletions(-) delete mode 100644 .github/workflows/pr12924-review-fixes.yml diff --git a/.github/workflows/pr12924-review-fixes.yml b/.github/workflows/pr12924-review-fixes.yml deleted file mode 100644 index 92c531ed9f54..000000000000 --- a/.github/workflows/pr12924-review-fixes.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: PR 12924 review fixes - -on: - push: - branches: - - fix-go-router-sdk-app-pages - -permissions: - contents: write - -jobs: - apply-and-validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - channel: master - cache: true - - name: Resolve go_router dependencies - working-directory: packages/go_router - run: flutter pub get - - name: Apply cache invalidation fix - run: | - python3 - <<'PY' - from pathlib import Path - p = Path('packages/go_router/lib/src/builder.dart') - s = p.read_text() - old = (' // This method can also be called if any of the page builders depend on\n' - ' // the context. In this case, make sure _pages are rebuilt.\n' - ' _pages = null;') - new = (' // This method can also be called if any of the page builders depend on\n' - ' // the context. In this case, make sure the app-type builders and pages are\n' - ' // recomputed from the current ancestor tree.\n' - ' _pageBuilderForAppType = null;\n' - ' _errorBuilderForAppType = null;\n' - ' _pages = null;') - if old not in s: - raise SystemExit('didChangeDependencies block not found') - p.write_text(s.replace(old, new, 1)) - PY - - name: Format changed Dart files - run: dart format packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - - name: Run full go_router test suite - working-directory: packages/go_router - run: flutter test - - name: Commit fix and remove temporary workflow - run: | - git config user.name "Yazan Arafeh" - git config user.email "32390922+yazanmg@users.noreply.github.com" - git add packages/go_router/lib/src/builder.dart packages/go_router/lib/src/pages/app_type.dart packages/go_router/lib/src/pages/cupertino.dart packages/go_router/lib/src/pages/material.dart packages/go_router/test/sdk_app_test.dart - git rm .github/workflows/pr12924-review-fixes.yml - git commit -m "Invalidate go_router app-type caches on dependency changes" - git push origin HEAD:fix-go-router-sdk-app-pages diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart index 969f1e71e179..b518b69188d7 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -189,7 +189,10 @@ class _CustomNavigatorState extends State<_CustomNavigator> { null => HeroController(), }; // This method can also be called if any of the page builders depend on - // the context. In this case, make sure _pages are rebuilt. + // the context. In this case, make sure the app-type builders and pages are + // recomputed from the current ancestor tree. + _pageBuilderForAppType = null; + _errorBuilderForAppType = null; _pages = null; } From b797b43f33eb074a05538a5288ac5e3395732008 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 23:17:34 +0300 Subject: [PATCH 15/18] Preserve go_router app adapter precedence --- .../go_router/lib/src/pages/app_type.dart | 28 +++++++++++-------- packages/go_router/test/sdk_app_test.dart | 6 ++-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/go_router/lib/src/pages/app_type.dart b/packages/go_router/lib/src/pages/app_type.dart index 8bb04de32f9a..1b7673fc3785 100644 --- a/packages/go_router/lib/src/pages/app_type.dart +++ b/packages/go_router/lib/src/pages/app_type.dart @@ -23,28 +23,32 @@ enum AppType { cupertinoUi, } -/// Finds the closest supported app implementation in the widget tree. +/// Finds the supported app implementation to use for the Navigator. +/// +/// Material apps retain precedence over Cupertino apps, matching the existing +/// go_router adapter selection behavior. Within each app family, the closest +/// supported implementation is used. AppType? appTypeOf(BuildContext context) { - AppType? result; + AppType? nearestCupertino; + AppType? material; context.visitAncestorElements((Element element) { final Widget widget = element.widget; if (widget is flutter_material.MaterialApp) { - result = AppType.sdkMaterial; + material = AppType.sdkMaterial; return false; } if (widget is material_ui.MaterialApp) { - result = AppType.materialUi; + material = AppType.materialUi; return false; } - if (widget is flutter_cupertino.CupertinoApp) { - result = AppType.sdkCupertino; - return false; - } - if (widget is cupertino_ui.CupertinoApp) { - result = AppType.cupertinoUi; - return false; + if (nearestCupertino == null) { + if (widget is flutter_cupertino.CupertinoApp) { + nearestCupertino = AppType.sdkCupertino; + } else if (widget is cupertino_ui.CupertinoApp) { + nearestCupertino = AppType.cupertinoUi; + } } return true; }); - return result; + return material ?? nearestCupertino; } diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart index 5cad7a7e8748..4eefd0e44596 100644 --- a/packages/go_router/test/sdk_app_test.dart +++ b/packages/go_router/test/sdk_app_test.dart @@ -70,7 +70,7 @@ void main() { }); testWidgets( - 'GoRoute.builder uses the closest supported app when Cupertino is nested in Material', + 'GoRoute.builder preserves Material precedence when Cupertino is nested in Material', (WidgetTester tester) async { final settings = await _pumpApp( tester, @@ -79,12 +79,12 @@ void main() { ), ); - expect(settings, isA>()); + expect(settings, isA>()); }, ); testWidgets( - 'GoRoute.builder uses the closest supported app when Material is nested in Cupertino', + 'GoRoute.builder uses Material when Material is nested in Cupertino', (WidgetTester tester) async { final settings = await _pumpApp( tester, From c5adb9134652ea5904f560c00c608a059d5e7daf Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 23:17:52 +0300 Subject: [PATCH 16/18] ci: validate go_router adapter precedence --- .../pr12924-precedence-validation.yml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/pr12924-precedence-validation.yml diff --git a/.github/workflows/pr12924-precedence-validation.yml b/.github/workflows/pr12924-precedence-validation.yml new file mode 100644 index 000000000000..53c5cf3f74a1 --- /dev/null +++ b/.github/workflows/pr12924-precedence-validation.yml @@ -0,0 +1,39 @@ +name: PR 12924 precedence validation + +on: + push: + branches: + - fix-go-router-sdk-app-pages + +permissions: + contents: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: subosito/flutter-action@v2 + with: + channel: master + cache: true + - name: Resolve go_router dependencies + working-directory: packages/go_router + run: flutter pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart + - name: Run targeted SDK app tests + working-directory: packages/go_router + run: flutter test test/sdk_app_test.dart + - name: Run full go_router test suite + working-directory: packages/go_router + run: flutter test + - name: Remove temporary validation workflow + run: | + git config user.name "Yazan Arafeh" + git config user.email "32390922+yazanmg@users.noreply.github.com" + git rm .github/workflows/pr12924-precedence-validation.yml + git commit -m "ci: remove PR 12924 precedence validation" + git push origin HEAD:fix-go-router-sdk-app-pages From 177c3a3ae65b14e08135ad029234b9ac55ba2734 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 23:19:24 +0300 Subject: [PATCH 17/18] ci: format before PR 12924 validation --- .github/workflows/pr12924-precedence-validation.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr12924-precedence-validation.yml b/.github/workflows/pr12924-precedence-validation.yml index 53c5cf3f74a1..b7c9a136a260 100644 --- a/.github/workflows/pr12924-precedence-validation.yml +++ b/.github/workflows/pr12924-precedence-validation.yml @@ -22,18 +22,19 @@ jobs: - name: Resolve go_router dependencies working-directory: packages/go_router run: flutter pub get - - name: Check formatting - run: dart format --output=none --set-exit-if-changed packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart + - name: Format changed Dart files + run: dart format packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart - name: Run targeted SDK app tests working-directory: packages/go_router run: flutter test test/sdk_app_test.dart - name: Run full go_router test suite working-directory: packages/go_router run: flutter test - - name: Remove temporary validation workflow + - name: Commit formatting and remove temporary validation workflow run: | git config user.name "Yazan Arafeh" git config user.email "32390922+yazanmg@users.noreply.github.com" + git add packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart git rm .github/workflows/pr12924-precedence-validation.yml - git commit -m "ci: remove PR 12924 precedence validation" + git commit -m "Validate go_router adapter precedence" git push origin HEAD:fix-go-router-sdk-app-pages From 637c5f303cfc4605bcb67eafa2fdefd8220ace44 Mon Sep 17 00:00:00 2001 From: Yazan Arafeh <32390922+yazanmg@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:20:37 +0000 Subject: [PATCH 18/18] Validate go_router adapter precedence --- .../pr12924-precedence-validation.yml | 40 ------------------- packages/go_router/test/sdk_app_test.dart | 23 +++++------ 2 files changed, 11 insertions(+), 52 deletions(-) delete mode 100644 .github/workflows/pr12924-precedence-validation.yml diff --git a/.github/workflows/pr12924-precedence-validation.yml b/.github/workflows/pr12924-precedence-validation.yml deleted file mode 100644 index b7c9a136a260..000000000000 --- a/.github/workflows/pr12924-precedence-validation.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: PR 12924 precedence validation - -on: - push: - branches: - - fix-go-router-sdk-app-pages - -permissions: - contents: write - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: subosito/flutter-action@v2 - with: - channel: master - cache: true - - name: Resolve go_router dependencies - working-directory: packages/go_router - run: flutter pub get - - name: Format changed Dart files - run: dart format packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart - - name: Run targeted SDK app tests - working-directory: packages/go_router - run: flutter test test/sdk_app_test.dart - - name: Run full go_router test suite - working-directory: packages/go_router - run: flutter test - - name: Commit formatting and remove temporary validation workflow - run: | - git config user.name "Yazan Arafeh" - git config user.email "32390922+yazanmg@users.noreply.github.com" - git add packages/go_router/lib/src/pages/app_type.dart packages/go_router/test/sdk_app_test.dart - git rm .github/workflows/pr12924-precedence-validation.yml - git commit -m "Validate go_router adapter precedence" - git push origin HEAD:fix-go-router-sdk-app-pages diff --git a/packages/go_router/test/sdk_app_test.dart b/packages/go_router/test/sdk_app_test.dart index 4eefd0e44596..13ff2eb4d72f 100644 --- a/packages/go_router/test/sdk_app_test.dart +++ b/packages/go_router/test/sdk_app_test.dart @@ -83,19 +83,18 @@ void main() { }, ); - testWidgets( - 'GoRoute.builder uses Material when Material is nested in Cupertino', - (WidgetTester tester) async { - final settings = await _pumpApp( - tester, - (GoRouter router) => flutter_cupertino.CupertinoApp( - home: flutter_material.MaterialApp.router(routerConfig: router), - ), - ); + testWidgets('GoRoute.builder uses Material when Material is nested in Cupertino', ( + WidgetTester tester, + ) async { + final settings = await _pumpApp( + tester, + (GoRouter router) => flutter_cupertino.CupertinoApp( + home: flutter_material.MaterialApp.router(routerConfig: router), + ), + ); - expect(settings, isA>()); - }, - ); + expect(settings, isA>()); + }); testWidgets('SDK MaterialApp uses the SDK Material error screen', (WidgetTester tester) async { final router = _errorRouter();