From 4b47f82a603ceee53a06b03dfd1893a8ff1bcbda Mon Sep 17 00:00:00 2001 From: Eimen2018 Date: Fri, 18 Sep 2026 01:24:46 +0400 Subject: [PATCH 1/4] fix(router): warn when customRouteBuilder is used with the Navigator 1 router The default router generator silently ignored CustomRoute.customRouteBuilder because Navigator 1's onGenerateRoute has no BuildContext to invoke the builder with. Emit a build-time warning pointing users to navigator2: true instead of dropping the option silently. Refs Stacked-Org/stacked#1164 Co-Authored-By: Claude Fable 5.1 --- .../router_class_builder_helper.dart | 11 ++ .../router_class_builder_helper_test.dart | 110 ++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart index a5c6348..c8a769c 100644 --- a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart +++ b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart @@ -156,6 +156,17 @@ mixin class RouterClassBuilderHelper { routeTypeImport: 'package:flutter/material.dart', ); } else if (route.routeType == RouteType.custom) { + // `customRouteBuilder` can only be invoked with a `BuildContext`, + // which Navigator 1's `onGenerateRoute(RouteSettings)` never has. + // Rather than silently dropping the option (or crashing a build that + // previously succeeded), let the developer know it's ignored here + // and point them at the router that does support it. + if (route.customRouteBuilder != null) { + // ignore: avoid_print + print( + '\nWARNING => CustomRoute.customRouteBuilder on \'${route.routeName}\' is only supported by the Navigator 2 router.\n' + 'Set `navigator2: true` in your @StackedApp / stacked config to use it; it is ignored by the default router.\n'); + } return route.getRouteRegisterCode( routeType: 'PageRouteBuilder', routeTypeImport: 'package:flutter/material.dart', diff --git a/test/router/router_class/router_class_builder_helper_test.dart b/test/router/router_class/router_class_builder_helper_test.dart index 28a17b4..5a51d16 100644 --- a/test/router/router_class/router_class_builder_helper_test.dart +++ b/test/router/router_class/router_class_builder_helper_test.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:stacked_generator/src/generators/router/generator/router_class/router_class_builder.dart'; import 'package:stacked_generator/src/generators/router/route_config/material_route_config.dart'; import 'package:stacked_generator/src/generators/router_common/models/importable_type.dart'; @@ -93,6 +95,114 @@ void main() { expect(generatedCode, contains('MaterialPageRoute'), reason: 'Should contain MaterialPageRoute for routes'); }, ); + + test( + 'a CustomRoute with a customRouteBuilder still generates the same PageRouteBuilder output ' + '(Navigator 1 cannot invoke customRouteBuilder without a BuildContext)', + () { + // This mirrors what RouteConfigResolver (shared by both the + // Navigator 1 and Navigator 2 generators) produces for a + // `CustomRoute` annotation: a plain `RouteConfig` with + // `routeType: RouteType.custom`. + const withoutBuilder = RouteConfig( + name: 'bottomSheetView', + pathName: 'bottomSheetPath', + className: 'BottomSheetClass', + classImport: 'ui/bottom_sheet_class.dart', + routeType: RouteType.custom, + ); + + final withBuilder = RouteConfig( + name: 'bottomSheetView', + pathName: 'bottomSheetPath', + className: 'BottomSheetClass', + classImport: 'ui/bottom_sheet_class.dart', + routeType: RouteType.custom, + customRouteBuilder: ResolvedType( + name: 'RouteBuilders.bottomSheetBuilder', + import: 'ui/route_builders.dart', + ), + ); + + final generatedWithout = routerClassBuilderHelper + .mapOfPages([withoutBuilder]).buildLibraryForClass; + final generatedWith = routerClassBuilderHelper + .mapOfPages([withBuilder]).buildLibraryForClass; + + expect(generatedWith, equals(generatedWithout), + reason: + 'Setting customRouteBuilder must not change the Navigator 1 output: ' + 'it still falls back to PageRouteBuilder unchanged, since Navigator 1 ' + 'has no BuildContext to invoke the builder with.'); + expect(generatedWith, contains('PageRouteBuilder')); + expect(generatedWith, isNot(contains('bottomSheetBuilder'))); + }, + ); + + test( + 'a CustomRoute with a customRouteBuilder emits a warning pointing to navigator2', + () async { + final routes = [ + RouteConfig( + name: 'bottomSheetView', + pathName: 'bottomSheetPath', + className: 'BottomSheetClass', + classImport: 'ui/bottom_sheet_class.dart', + routeType: RouteType.custom, + customRouteBuilder: ResolvedType( + name: 'RouteBuilders.bottomSheetBuilder', + import: 'ui/route_builders.dart', + ), + ), + ]; + + final printedLines = []; + await runZoned( + () async { + routerClassBuilderHelper.mapOfPages(routes); + }, + zoneSpecification: ZoneSpecification( + print: (self, parent, zone, line) => printedLines.add(line), + ), + ); + + final combined = printedLines.join('\n'); + expect(combined, contains('WARNING')); + expect(combined, contains('customRouteBuilder')); + expect(combined, contains(routes.first.routeName)); + expect(combined, contains('navigator2: true')); + expect(combined, contains('Navigator 2')); + }, + ); + + test( + 'a CustomRoute without a customRouteBuilder does not emit any warning', + () async { + final routes = [ + const RouteConfig( + name: 'bottomSheetView', + pathName: 'bottomSheetPath', + className: 'BottomSheetClass', + classImport: 'ui/bottom_sheet_class.dart', + routeType: RouteType.custom, + ), + ]; + + final printedLines = []; + await runZoned( + () async { + routerClassBuilderHelper.mapOfPages(routes); + }, + zoneSpecification: ZoneSpecification( + print: (self, parent, zone, line) => printedLines.add(line), + ), + ); + + expect(printedLines, isEmpty, + reason: + 'No warning should be printed when customRouteBuilder is not set'); + }, + ); }); }); } From d7afbeff86bcafed9c05145325d5b95f3821c10b Mon Sep 17 00:00:00 2001 From: Eimen2018 Date: Sat, 19 Sep 2026 01:24:00 +0400 Subject: [PATCH 2/4] chore: trim comments Co-Authored-By: Claude Fable 5.1 --- .../generator/router_class/router_class_builder_helper.dart | 6 +----- .../router_class/router_class_builder_helper_test.dart | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart index c8a769c..3451ecd 100644 --- a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart +++ b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart @@ -156,11 +156,7 @@ mixin class RouterClassBuilderHelper { routeTypeImport: 'package:flutter/material.dart', ); } else if (route.routeType == RouteType.custom) { - // `customRouteBuilder` can only be invoked with a `BuildContext`, - // which Navigator 1's `onGenerateRoute(RouteSettings)` never has. - // Rather than silently dropping the option (or crashing a build that - // previously succeeded), let the developer know it's ignored here - // and point them at the router that does support it. + // Navigator 1 has no BuildContext to call customRouteBuilder with. if (route.customRouteBuilder != null) { // ignore: avoid_print print( diff --git a/test/router/router_class/router_class_builder_helper_test.dart b/test/router/router_class/router_class_builder_helper_test.dart index 5a51d16..cd1e89c 100644 --- a/test/router/router_class/router_class_builder_helper_test.dart +++ b/test/router/router_class/router_class_builder_helper_test.dart @@ -100,10 +100,6 @@ void main() { 'a CustomRoute with a customRouteBuilder still generates the same PageRouteBuilder output ' '(Navigator 1 cannot invoke customRouteBuilder without a BuildContext)', () { - // This mirrors what RouteConfigResolver (shared by both the - // Navigator 1 and Navigator 2 generators) produces for a - // `CustomRoute` annotation: a plain `RouteConfig` with - // `routeType: RouteType.custom`. const withoutBuilder = RouteConfig( name: 'bottomSheetView', pathName: 'bottomSheetPath', From d2832113c273d50445dbb9c6cf16caa8c9342df5 Mon Sep 17 00:00:00 2001 From: Eimen2018 Date: Sat, 19 Sep 2026 20:11:30 +0400 Subject: [PATCH 3/4] fix(router): point customRouteBuilder warning at the build.yaml option navigator2 is a stacked_generator builder option, not a @StackedApp parameter. Co-Authored-By: Claude Fable 5.1 --- .../generator/router_class/router_class_builder_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart index 3451ecd..5369e70 100644 --- a/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart +++ b/lib/src/generators/router/generator/router_class/router_class_builder_helper.dart @@ -161,7 +161,7 @@ mixin class RouterClassBuilderHelper { // ignore: avoid_print print( '\nWARNING => CustomRoute.customRouteBuilder on \'${route.routeName}\' is only supported by the Navigator 2 router.\n' - 'Set `navigator2: true` in your @StackedApp / stacked config to use it; it is ignored by the default router.\n'); + 'Set `navigator2: true` in the stacked_generator|stackedRouterGenerator builder options in build.yaml to use it; it is ignored by the default router.\n'); } return route.getRouteRegisterCode( routeType: 'PageRouteBuilder', From 80d74735243dc18beea4734928c49898ed7066af Mon Sep 17 00:00:00 2001 From: Eimen2018 Date: Tue, 22 Sep 2026 03:26:59 +0400 Subject: [PATCH 4/4] test: keep the customRouteBuilder warning out of test output Co-Authored-By: Claude Fable 5.1 --- .../router_class/router_class_builder_helper_test.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/router/router_class/router_class_builder_helper_test.dart b/test/router/router_class/router_class_builder_helper_test.dart index cd1e89c..5329787 100644 --- a/test/router/router_class/router_class_builder_helper_test.dart +++ b/test/router/router_class/router_class_builder_helper_test.dart @@ -122,8 +122,13 @@ void main() { final generatedWithout = routerClassBuilderHelper .mapOfPages([withoutBuilder]).buildLibraryForClass; - final generatedWith = routerClassBuilderHelper - .mapOfPages([withBuilder]).buildLibraryForClass; + final generatedWith = runZoned( + () => routerClassBuilderHelper + .mapOfPages([withBuilder]).buildLibraryForClass, + zoneSpecification: ZoneSpecification( + print: (self, parent, zone, line) {}, + ), + ); expect(generatedWith, equals(generatedWithout), reason: