diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index 11aeb62a35d9..3cd835996966 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Adds Support for StatefulShellRoute + ## 2.2.5 * Fixes a bug where shell routes without const constructor were not generated correctly. diff --git a/packages/go_router_builder/README.md b/packages/go_router_builder/README.md index 797231c6be8f..73faf973937c 100644 --- a/packages/go_router_builder/README.md +++ b/packages/go_router_builder/README.md @@ -8,12 +8,12 @@ To use `go_router_builder`, you need to have the following dependencies in ```yaml dependencies: # ...along with your other dependencies - go_router: ^7.0.0 + go_router: ^9.0.3 dev_dependencies: # ...along with your other dev-dependencies build_runner: ^2.0.0 - go_router_builder: ^2.0.0 + go_router_builder: ^2.3.0 ``` ### Source code diff --git a/packages/go_router_builder/example/lib/shell_route_with_keys_example.g.dart b/packages/go_router_builder/example/lib/shell_route_with_keys_example.g.dart index 31f9d4bca6d4..6151889582d0 100644 --- a/packages/go_router_builder/example/lib/shell_route_with_keys_example.g.dart +++ b/packages/go_router_builder/example/lib/shell_route_with_keys_example.g.dart @@ -26,8 +26,8 @@ RouteBase get $myShellRouteData => ShellRouteData.$route( routes: [ GoRouteData.$route( path: ':id', - factory: $UserRouteDataExtension._fromState, parentNavigatorKey: UserRouteData.$parentNavigatorKey, + factory: $UserRouteDataExtension._fromState, ), ], ), diff --git a/packages/go_router_builder/example/lib/stateful_shell_route_example.dart b/packages/go_router_builder/example/lib/stateful_shell_route_example.dart new file mode 100644 index 000000000000..9e9748e870dc --- /dev/null +++ b/packages/go_router_builder/example/lib/stateful_shell_route_example.dart @@ -0,0 +1,266 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +part 'stateful_shell_route_example.g.dart'; + +final GlobalKey _sectionANavigatorKey = + GlobalKey(debugLabel: 'sectionANav'); +void main() => runApp(App()); + +class App extends StatelessWidget { + App({super.key}); + + @override + Widget build(BuildContext context) => MaterialApp.router( + routerConfig: _router, + ); + + final GoRouter _router = GoRouter( + routes: $appRoutes, + initialLocation: '/detailsA', + ); +} + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: const Text('foo')), + ); +} + +@TypedStatefulShellRoute( + branches: >[ + TypedStatefulShellBranch( + routes: >[ + TypedGoRoute(path: '/detailsA'), + ], + ), + TypedStatefulShellBranch( + routes: >[ + TypedGoRoute(path: '/detailsB'), + ], + ), + ], +) +class MyShellRouteData extends StatefulShellRouteData { + const MyShellRouteData(); + + @override + Widget builder( + BuildContext context, + GoRouterState state, + StatefulNavigationShell navigationShell, + ) { + return navigationShell; + } + + static const String $restorationScopeId = 'restorationScopeId'; + + static Widget $navigatorContainerBuilder(BuildContext context, + StatefulNavigationShell navigationShell, List children) { + return ScaffoldWithNavBar( + navigationShell: navigationShell, + children: children, + ); + } +} + +class BranchAData extends StatefulShellBranchData { + const BranchAData(); +} + +class BranchBData extends StatefulShellBranchData { + const BranchBData(); + + static final GlobalKey $navigatorKey = _sectionANavigatorKey; + static const String $restorationScopeId = 'restorationScopeId'; +} + +class DetailsARouteData extends GoRouteData { + const DetailsARouteData(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const DetailsScreen(label: 'A'); + } +} + +class DetailsBRouteData extends GoRouteData { + const DetailsBRouteData(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const DetailsScreen(label: 'B'); + } +} + +/// Builds the "shell" for the app by building a Scaffold with a +/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. +class ScaffoldWithNavBar extends StatelessWidget { + /// Constructs an [ScaffoldWithNavBar]. + const ScaffoldWithNavBar({ + required this.navigationShell, + required this.children, + Key? key, + }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); + + /// The navigation shell and container for the branch Navigators. + final StatefulNavigationShell navigationShell; + + /// The children (branch Navigators) to display in a custom container + /// ([AnimatedBranchContainer]). + final List children; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: AnimatedBranchContainer( + currentIndex: navigationShell.currentIndex, + children: children, + ), + bottomNavigationBar: BottomNavigationBar( + // Here, the items of BottomNavigationBar are hard coded. In a real + // world scenario, the items would most likely be generated from the + // branches of the shell route, which can be fetched using + // `navigationShell.route.branches`. + items: const [ + BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'), + BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'), + ], + currentIndex: navigationShell.currentIndex, + onTap: (int index) => _onTap(context, index), + ), + ); + } + + /// Navigate to the current location of the branch at the provided index when + /// tapping an item in the BottomNavigationBar. + void _onTap(BuildContext context, int index) { + // When navigating to a new branch, it's recommended to use the goBranch + // method, as doing so makes sure the last navigation state of the + // Navigator for the branch is restored. + navigationShell.goBranch( + index, + // A common pattern when using bottom navigation bars is to support + // navigating to the initial location when tapping the item that is + // already active. This example demonstrates how to support this behavior, + // using the initialLocation parameter of goBranch. + initialLocation: index == navigationShell.currentIndex, + ); + } +} + +/// Custom branch Navigator container that provides animated transitions +/// when switching branches. +class AnimatedBranchContainer extends StatelessWidget { + /// Creates a AnimatedBranchContainer + const AnimatedBranchContainer( + {super.key, required this.currentIndex, required this.children}); + + /// The index (in [children]) of the branch Navigator to display. + final int currentIndex; + + /// The children (branch Navigators) to display in this container. + final List children; + + @override + Widget build(BuildContext context) { + return Stack( + children: children.mapIndexed( + (int index, Widget navigator) { + return AnimatedScale( + scale: index == currentIndex ? 1 : 1.5, + duration: const Duration(milliseconds: 400), + child: AnimatedOpacity( + opacity: index == currentIndex ? 1 : 0, + duration: const Duration(milliseconds: 400), + child: _branchNavigatorWrapper(index, navigator), + ), + ); + }, + ).toList()); + } + + Widget _branchNavigatorWrapper(int index, Widget navigator) => IgnorePointer( + ignoring: index != currentIndex, + child: TickerMode( + enabled: index == currentIndex, + child: navigator, + ), + ); +} + +/// The details screen for either the A or B screen. +class DetailsScreen extends StatefulWidget { + /// Constructs a [DetailsScreen]. + const DetailsScreen({ + required this.label, + this.param, + this.extra, + super.key, + }); + + /// The label to display in the center of the screen. + final String label; + + /// Optional param + final String? param; + + /// Optional extra object + final Object? extra; + @override + State createState() => DetailsScreenState(); +} + +/// The state for DetailsScreen +class DetailsScreenState extends State { + int _counter = 0; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Details Screen - ${widget.label}'), + ), + body: _build(context), + ); + } + + Widget _build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Details for ${widget.label} - Counter: $_counter', + style: Theme.of(context).textTheme.titleLarge), + const Padding(padding: EdgeInsets.all(4)), + TextButton( + onPressed: () { + setState(() { + _counter++; + }); + }, + child: const Text('Increment counter'), + ), + const Padding(padding: EdgeInsets.all(8)), + if (widget.param != null) + Text('Parameter: ${widget.param!}', + style: Theme.of(context).textTheme.titleMedium), + const Padding(padding: EdgeInsets.all(8)), + if (widget.extra != null) + Text('Extra: ${widget.extra!}', + style: Theme.of(context).textTheme.titleMedium), + ], + ), + ); + } +} diff --git a/packages/go_router_builder/example/lib/stateful_shell_route_example.g.dart b/packages/go_router_builder/example/lib/stateful_shell_route_example.g.dart new file mode 100644 index 000000000000..9be15d9859a4 --- /dev/null +++ b/packages/go_router_builder/example/lib/stateful_shell_route_example.g.dart @@ -0,0 +1,80 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: always_specify_types, public_member_api_docs + +part of 'stateful_shell_route_example.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $myShellRouteData, + ]; + +RouteBase get $myShellRouteData => StatefulShellRouteData.$route( + restorationScopeId: MyShellRouteData.$restorationScopeId, + navigatorContainerBuilder: MyShellRouteData.$navigatorContainerBuilder, + factory: $MyShellRouteDataExtension._fromState, + branches: [ + StatefulShellBranchData.$branch( + routes: [ + GoRouteData.$route( + path: '/detailsA', + factory: $DetailsARouteDataExtension._fromState, + ), + ], + ), + StatefulShellBranchData.$branch( + navigatorKey: BranchBData.$navigatorKey, + restorationScopeId: BranchBData.$restorationScopeId, + routes: [ + GoRouteData.$route( + path: '/detailsB', + factory: $DetailsBRouteDataExtension._fromState, + ), + ], + ), + ], + ); + +extension $MyShellRouteDataExtension on MyShellRouteData { + static MyShellRouteData _fromState(GoRouterState state) => + const MyShellRouteData(); +} + +extension $DetailsARouteDataExtension on DetailsARouteData { + static DetailsARouteData _fromState(GoRouterState state) => + const DetailsARouteData(); + + String get location => GoRouteData.$location( + '/detailsA', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $DetailsBRouteDataExtension on DetailsBRouteData { + static DetailsBRouteData _fromState(GoRouterState state) => + const DetailsBRouteData(); + + String get location => GoRouteData.$location( + '/detailsB', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/packages/go_router_builder/example/pubspec.yaml b/packages/go_router_builder/example/pubspec.yaml index 3dea2475de71..1cc07ea34302 100644 --- a/packages/go_router_builder/example/pubspec.yaml +++ b/packages/go_router_builder/example/pubspec.yaml @@ -6,6 +6,7 @@ environment: sdk: ">=2.18.0 <4.0.0" dependencies: + collection: ^1.15.0 flutter: sdk: flutter go_router: ^10.0.0 diff --git a/packages/go_router_builder/example/test/stateful_shell_route_test.dart b/packages/go_router_builder/example/test/stateful_shell_route_test.dart new file mode 100644 index 000000000000..f885e54c531e --- /dev/null +++ b/packages/go_router_builder/example/test/stateful_shell_route_test.dart @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router_builder_example/stateful_shell_route_example.dart'; + +void main() { + testWidgets('Navigate between section A and section B', + (WidgetTester tester) async { + await tester.pumpWidget(App()); + expect(find.text('Details for A - Counter: 0'), findsOneWidget); + + await tester.tap(find.text('Increment counter')); + await tester.pumpAndSettle(); + expect(find.text('Details for A - Counter: 1'), findsOneWidget); + + await tester.tap(find.text('Section B')); + await tester.pumpAndSettle(); + expect(find.text('Details for B - Counter: 0'), findsOneWidget); + + await tester.tap(find.text('Section A')); + await tester.pumpAndSettle(); + expect(find.text('Details for A - Counter: 1'), findsOneWidget); + }); +} diff --git a/packages/go_router_builder/lib/src/go_router_generator.dart b/packages/go_router_builder/lib/src/go_router_generator.dart index cf57329f3a34..e094d1f98edd 100644 --- a/packages/go_router_builder/lib/src/go_router_generator.dart +++ b/packages/go_router_builder/lib/src/go_router_generator.dart @@ -16,6 +16,8 @@ const String _routeDataUrl = 'package:go_router/src/route_data.dart'; const Map _annotations = { 'TypedGoRoute': 'GoRouteData', 'TypedShellRoute': 'ShellRouteData', + 'TypedStatefulShellBranch': 'StatefulShellBranchData', + 'TypedStatefulShellRoute': 'StatefulShellRouteData', }; /// A [Generator] for classes annotated with a typed go route annotation. diff --git a/packages/go_router_builder/lib/src/route_config.dart b/packages/go_router_builder/lib/src/route_config.dart index 1d607761a98e..5925c11541fb 100644 --- a/packages/go_router_builder/lib/src/route_config.dart +++ b/packages/go_router_builder/lib/src/route_config.dart @@ -38,14 +38,17 @@ class InfoIterable extends IterableBase { class ShellRouteConfig extends RouteBaseConfig { ShellRouteConfig._({ required this.navigatorKey, + required this.parentNavigatorKey, required super.routeDataClass, required super.parent, - required super.parentNavigatorKey, }) : super._(); /// The command for calling the navigator key getter from the ShellRouteData. final String? navigatorKey; + /// The parent navigator key. + final String? parentNavigatorKey; + @override Iterable classDeclarations() { if (routeDataClass.unnamedConstructor == null) { @@ -68,10 +71,95 @@ class ShellRouteConfig extends RouteBaseConfig { @override String get routeConstructorParameters => - navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'; + '${navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'}' + '${parentNavigatorKey == null ? '' : 'parentNavigatorKey: $parentNavigatorKey,'}'; + + @override + String get factorConstructorParameters => + 'factory: $_extensionName._fromState,'; @override String get routeDataClassName => 'ShellRouteData'; + + @override + String get dataConvertionFunctionName => r'$route'; +} + +/// The configuration to generate class declarations for a StatefulShellRouteData. +class StatefulShellRouteConfig extends RouteBaseConfig { + StatefulShellRouteConfig._({ + required this.parentNavigatorKey, + required super.routeDataClass, + required super.parent, + required this.navigatorContainerBuilder, + required this.restorationScopeId, + }) : super._(); + + /// The parent navigator key. + final String? parentNavigatorKey; + + /// The navigator container builder. + final String? navigatorContainerBuilder; + + /// The restoration scope id. + final String? restorationScopeId; + + @override + Iterable classDeclarations() => [ + ''' +extension $_extensionName on $_className { + static $_className _fromState(GoRouterState state) => const $_className(); +} +''' + ]; + + @override + String get routeConstructorParameters => + '${parentNavigatorKey == null ? '' : 'parentNavigatorKey: $parentNavigatorKey,'}' + '${restorationScopeId == null ? '' : 'restorationScopeId: $restorationScopeId,'}' + '${navigatorContainerBuilder == null ? '' : 'navigatorContainerBuilder: $navigatorContainerBuilder,'}'; + + @override + String get factorConstructorParameters => + 'factory: $_extensionName._fromState,'; + + @override + String get routeDataClassName => 'StatefulShellRouteData'; + + @override + String get dataConvertionFunctionName => r'$route'; +} + +/// The configuration to generate class declarations for a StatefulShellBranchData. +class StatefulShellBranchConfig extends RouteBaseConfig { + StatefulShellBranchConfig._({ + required this.navigatorKey, + required super.routeDataClass, + required super.parent, + this.restorationScopeId, + }) : super._(); + + /// The command for calling the navigator key getter from the ShellRouteData. + final String? navigatorKey; + + /// The restoration scope id. + final String? restorationScopeId; + + @override + Iterable classDeclarations() => []; + + @override + String get factorConstructorParameters => ''; + @override + String get routeConstructorParameters => + '${navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'}' + '${restorationScopeId == null ? '' : 'restorationScopeId: $restorationScopeId,'}'; + + @override + String get routeDataClassName => 'StatefulShellBranchData'; + + @override + String get dataConvertionFunctionName => r'$branch'; } /// The configuration to generate class declarations for a GoRouteData. @@ -79,9 +167,9 @@ class GoRouteConfig extends RouteBaseConfig { GoRouteConfig._({ required this.path, required this.name, + required this.parentNavigatorKey, required super.routeDataClass, required super.parent, - required super.parentNavigatorKey, }) : super._(); /// The path of the GoRoute to be created by this configuration. @@ -90,6 +178,9 @@ class GoRouteConfig extends RouteBaseConfig { /// The name of the GoRoute to be created by this configuration. final String? name; + /// The parent navigator key. + final String? parentNavigatorKey; + late final Set _pathParams = pathParametersFromPattern(_rawJoinedPath); @@ -295,14 +386,22 @@ extension $_extensionName on $_className { return enumParamTypes.map(_enumMapConst); } + @override + String get factorConstructorParameters => + 'factory: $_extensionName._fromState,'; + @override String get routeConstructorParameters => ''' path: ${escapeDartString(path)}, ${name != null ? 'name: ${escapeDartString(name!)},' : ''} + ${parentNavigatorKey == null ? '' : 'parentNavigatorKey: $parentNavigatorKey,'} '''; @override String get routeDataClassName => 'GoRouteData'; + + @override + String get dataConvertionFunctionName => r'$route'; } /// Represents a `TypedGoRoute` annotation to the builder. @@ -310,7 +409,6 @@ abstract class RouteBaseConfig { RouteBaseConfig._({ required this.routeDataClass, required this.parent, - required this.parentNavigatorKey, }); /// Creates a new [RouteBaseConfig] represented the annotation data in [reader]. @@ -342,7 +440,7 @@ abstract class RouteBaseConfig { // TODO(stuartmorgan): Remove this ignore once 'analyze' can be set to // 5.2+ (when Flutter 3.4+ is on stable). // ignore: deprecated_member_use - final bool isShellRoute = type.element.name == 'TypedShellRoute'; + final String typeName = type.element.name; final DartType typeParamType = type.typeArguments.single; if (typeParamType is! InterfaceType) { throw InvalidGenerationSourceError( @@ -359,43 +457,81 @@ abstract class RouteBaseConfig { final InterfaceElement classElement = typeParamType.element; final RouteBaseConfig value; - if (isShellRoute) { - value = ShellRouteConfig._( - routeDataClass: classElement, - parent: parent, - navigatorKey: _generateNavigatorKeyGetterCode( - classElement, - keyName: r'$navigatorKey', - ), - parentNavigatorKey: _generateNavigatorKeyGetterCode( - classElement, - keyName: r'$parentNavigatorKey', - ), - ); - } else { - final ConstantReader pathValue = reader.read('path'); - if (pathValue.isNull) { - throw InvalidGenerationSourceError( - 'Missing `path` value on annotation.', - element: element, + switch (typeName) { + case 'TypedShellRoute': + value = ShellRouteConfig._( + routeDataClass: classElement, + parent: parent, + navigatorKey: _generateParameterGetterCode( + classElement, + parameterName: r'$navigatorKey', + ), + parentNavigatorKey: _generateParameterGetterCode( + classElement, + parameterName: r'$parentNavigatorKey', + ), ); - } - - final ConstantReader nameValue = reader.read('name'); - value = GoRouteConfig._( - path: pathValue.stringValue, - name: nameValue.isNull ? null : nameValue.stringValue, - routeDataClass: classElement, - parent: parent, - parentNavigatorKey: _generateNavigatorKeyGetterCode( - classElement, - keyName: r'$parentNavigatorKey', - ), - ); + break; + case 'TypedStatefulShellRoute': + value = StatefulShellRouteConfig._( + routeDataClass: classElement, + parent: parent, + parentNavigatorKey: _generateParameterGetterCode( + classElement, + parameterName: r'$parentNavigatorKey', + ), + restorationScopeId: _generateParameterGetterCode( + classElement, + parameterName: r'$restorationScopeId', + ), + navigatorContainerBuilder: _generateParameterGetterCode( + classElement, + parameterName: r'$navigatorContainerBuilder', + ), + ); + break; + case 'TypedStatefulShellBranch': + value = StatefulShellBranchConfig._( + routeDataClass: classElement, + parent: parent, + navigatorKey: _generateParameterGetterCode( + classElement, + parameterName: r'$navigatorKey', + ), + restorationScopeId: _generateParameterGetterCode( + classElement, + parameterName: r'$restorationScopeId', + ), + ); + break; + case 'TypedGoRoute': + final ConstantReader pathValue = reader.read('path'); + if (pathValue.isNull) { + throw InvalidGenerationSourceError( + 'Missing `path` value on annotation.', + element: element, + ); + } + final ConstantReader nameValue = reader.read('name'); + value = GoRouteConfig._( + path: pathValue.stringValue, + name: nameValue.isNull ? null : nameValue.stringValue, + routeDataClass: classElement, + parent: parent, + parentNavigatorKey: _generateParameterGetterCode( + classElement, + parameterName: r'$parentNavigatorKey', + ), + ); + break; + default: + throw UnsupportedError('Unrecognized type $typeName'); } - value._children.addAll(reader.read('routes').listValue.map( - (DartObject e) => RouteBaseConfig._fromAnnotation( + value._children.addAll(reader + .read(_generateChildrenGetterName(typeName)) + .listValue + .map((DartObject e) => RouteBaseConfig._fromAnnotation( ConstantReader(e), element, value))); return value; @@ -409,40 +545,55 @@ abstract class RouteBaseConfig { /// The parent of this route config. final RouteBaseConfig? parent; - /// The parent navigator key string that is used for initialize the - /// `RouteBase` class this config generates. - final String? parentNavigatorKey; + static String _generateChildrenGetterName(String name) { + return (name == 'TypedStatefulShellRoute' || + name == 'StatefulShellRouteData') + ? 'branches' + : 'routes'; + } - static String? _generateNavigatorKeyGetterCode( - InterfaceElement classElement, { - required String keyName, - }) { + static String? _generateParameterGetterCode(InterfaceElement classElement, + {required String parameterName}) { final String? fieldDisplayName = classElement.fields .where((FieldElement element) { - final DartType type = element.type; - if (!element.isStatic || - element.name != keyName || - type is! ParameterizedType) { + if (!element.isStatic || element.name != parameterName) { return false; } - final List typeArguments = type.typeArguments; - if (typeArguments.length != 1) { - return false; + if (parameterName.toLowerCase().contains('navigatorkey')) { + final DartType type = element.type; + if (type is! ParameterizedType) { + return false; + } + final List typeArguments = type.typeArguments; + if (typeArguments.length != 1) { + return false; + } + final DartType typeArgument = typeArguments.single; + if (typeArgument.getDisplayString(withNullability: false) != + 'NavigatorState') { + return false; + } } - final DartType typeArgument = typeArguments.single; - if (typeArgument.getDisplayString(withNullability: false) == - 'NavigatorState') { - return true; - } - return false; + return true; }) .map((FieldElement e) => e.displayName) .firstOrNull; - if (fieldDisplayName == null) { - return null; + if (fieldDisplayName != null) { + return '${classElement.name}.$fieldDisplayName'; } - return '${classElement.name}.$fieldDisplayName'; + + final String? methodDisplayName = classElement.methods + .where((MethodElement element) { + return element.isStatic && element.name == parameterName; + }) + .map((MethodElement e) => e.displayName) + .firstOrNull; + + if (methodDisplayName != null) { + return '${classElement.name}.$methodDisplayName'; + } + return null; } /// Generates all of the members that correspond to `this`. @@ -496,16 +647,13 @@ RouteBase get $_routeGetterName => ${_invokesRouteConstructor()}; final String routesBit = _children.isEmpty ? '' : ''' -routes: [${_children.map((RouteBaseConfig e) => '${e._invokesRouteConstructor()},').join()}], +${_generateChildrenGetterName(routeDataClassName)}: [${_children.map((RouteBaseConfig e) => '${e._invokesRouteConstructor()},').join()}], '''; - final String parentNavigatorKeyParameter = parentNavigatorKey == null - ? '' - : 'parentNavigatorKey: $parentNavigatorKey,'; + return ''' -$routeDataClassName.\$route( +$routeDataClassName.$dataConvertionFunctionName( $routeConstructorParameters - factory: $_extensionName._fromState, - $parentNavigatorKeyParameter + $factorConstructorParameters $routesBit ) '''; @@ -518,6 +666,14 @@ $routeDataClassName.\$route( @protected String get routeDataClassName; + /// The function name of `RouteData` to get Routes or branches. + @protected + String get dataConvertionFunctionName; + + /// Additional factory constructor. + @protected + String get factorConstructorParameters; + /// Additional constructor parameter for invoking route constructor. @protected String get routeConstructorParameters; diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml index dac268f794b7..c3beffd473c6 100644 --- a/packages/go_router_builder/pubspec.yaml +++ b/packages/go_router_builder/pubspec.yaml @@ -2,7 +2,7 @@ name: go_router_builder description: >- A builder that supports generated strongly-typed route helpers for package:go_router -version: 2.2.5 +version: 2.3.0 repository: https://github.com/flutter/packages/tree/main/packages/go_router_builder issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router_builder%22