-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[go_router_builder] Add go_router StatefulShellRoute support to go_router_builder #4238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
990c293
4918014
c6bde6d
52ce037
b9c8080
9005a8c
8cf69b6
5963020
4f60cd6
e4fc526
d328021
bc19f53
f827680
6a09020
bb6064b
441eb78
8b4e2de
a2ef5d2
13865ac
e3d109d
51ae0a0
bfd36ef
03c069b
0da2339
06c7e67
ecbc5f5
9808f62
6044e32
3c2ed3b
1e78def
eecb67e
2e63519
cfb1de8
7fa8523
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…er-stateful
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,174 +34,50 @@ class InfoIterable extends IterableBase<String> { | |
| Iterator<String> get iterator => members.iterator; | ||
| } | ||
|
|
||
| /// Represents a `TypedGoRoute` annotation to the builder. | ||
| class RouteConfig { | ||
| RouteConfig._( | ||
| this._path, | ||
| this._name, | ||
| this._routeDataClass, | ||
| this._parent, | ||
| this._key, | ||
| this._typeName, | ||
| ); | ||
| /// The configuration to generate class declarations for a ShellRouteData. | ||
| class ShellRouteConfig extends RouteBaseConfig { | ||
| ShellRouteConfig._({ | ||
| required this.navigatorKey, | ||
| required super.routeDataClass, | ||
| required super.parent, | ||
| required super.parentNavigatorKey, | ||
| }) : super._(); | ||
|
|
||
| /// The command for calling the navigator key getter from the ShellRouteData. | ||
| final String? navigatorKey; | ||
|
|
||
| if (element != definition._routeDataClass) { | ||
| throw InvalidGenerationSourceError( | ||
| 'The @TypedGoRoute annotation must have a type parameter that matches ' | ||
| 'the annotated element.', | ||
| element: element, | ||
| ); | ||
| } | ||
|
|
||
| return definition; | ||
| } | ||
|
|
||
| factory RouteConfig._fromAnnotation( | ||
| ConstantReader reader, | ||
| InterfaceElement element, | ||
| RouteConfig? parent, | ||
| ) { | ||
| assert(!reader.isNull, 'reader should not be null'); | ||
| final InterfaceType type = reader.objectValue.type! as InterfaceType; | ||
| // 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 String typeName = type.element.name; | ||
|
|
||
| String? path; | ||
| String? name; | ||
|
|
||
| if (!typeName.contains('Shell')) { | ||
| final ConstantReader pathValue = reader.read('path'); | ||
| if (pathValue.isNull) { | ||
| throw InvalidGenerationSourceError( | ||
| 'Missing `path` value on annotation.', | ||
| element: element, | ||
| ); | ||
| } | ||
| path = pathValue.stringValue; | ||
|
|
||
| final ConstantReader nameValue = reader.read('name'); | ||
| name = nameValue.isNull ? null : nameValue.stringValue; | ||
| } | ||
|
|
||
| final DartType typeParamType = type.typeArguments.single; | ||
| if (typeParamType is! InterfaceType) { | ||
| throw InvalidGenerationSourceError( | ||
| 'The type parameter on one of the @TypedGoRoute declarations could not ' | ||
| 'be parsed.', | ||
| element: element, | ||
| ); | ||
| } | ||
|
|
||
| // TODO(kevmoo): validate that this MUST be a subtype of `GoRouteData` | ||
| // 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 InterfaceElement classElement = typeParamType.element; | ||
|
|
||
| final RouteConfig value = RouteConfig._( | ||
| path ?? '', | ||
| name, | ||
| classElement, | ||
| parent, | ||
| _generateNavigatorKeyGetterCode( | ||
| classElement, | ||
| keyName: typeName.contains('Shell') | ||
| ? r'$navigatorKey' | ||
| : r'$parentNavigatorKey', | ||
| ), | ||
| typeName, | ||
| ); | ||
| if (typeName == 'TypedStatefulShellRoute') { | ||
| value._children.addAll(reader.read('branches').listValue.map( | ||
| (DartObject e) => | ||
| RouteConfig._fromAnnotation(ConstantReader(e), element, value))); | ||
| } else { | ||
| value._children.addAll(reader.read('routes').listValue.map( | ||
| (DartObject e) => | ||
| RouteConfig._fromAnnotation(ConstantReader(e), element, value))); | ||
| } | ||
|
|
||
| return value; | ||
| } | ||
|
|
||
| final List<RouteConfig> _children = <RouteConfig>[]; | ||
| final String _path; | ||
| final String? _name; | ||
| final InterfaceElement _routeDataClass; | ||
| final RouteConfig? _parent; | ||
| final String? _key; | ||
| final String _typeName; | ||
|
|
||
| static String? _generateNavigatorKeyGetterCode( | ||
| InterfaceElement classElement, { | ||
| required String keyName, | ||
| }) { | ||
| final String? fieldDisplayName = classElement.fields | ||
| .where((FieldElement element) { | ||
| final DartType type = element.type; | ||
| if (!element.isStatic || | ||
| element.name != keyName || | ||
| type is! ParameterizedType) { | ||
| return false; | ||
| } | ||
| final List<DartType> typeArguments = type.typeArguments; | ||
| if (typeArguments.length != 1) { | ||
| return false; | ||
| } | ||
| final DartType typeArgument = typeArguments.single; | ||
| if (typeArgument.getDisplayString(withNullability: false) == | ||
| 'NavigatorState') { | ||
| return true; | ||
| } | ||
| return false; | ||
| }) | ||
| .map<String>((FieldElement e) => e.displayName) | ||
| .firstOrNull; | ||
|
|
||
| if (fieldDisplayName == null) { | ||
| return null; | ||
| } | ||
| return '${classElement.name}.$fieldDisplayName'; | ||
| } | ||
|
|
||
| /// Generates all of the members that correspond to `this`. | ||
| InfoIterable generateMembers() => InfoIterable._( | ||
| members: _generateMembers().toList(), | ||
| routeGetterName: _routeGetterName, | ||
| ); | ||
|
|
||
| Iterable<String> _generateMembers() sync* { | ||
| final List<String> items = <String>[ | ||
| _rootDefinition(), | ||
| ]; | ||
| @override | ||
| Iterable<String> classDeclarations() => <String>[ | ||
| ''' | ||
| extension $_extensionName on $_className { | ||
| static $_className _fromState(GoRouterState state) => const $_className(); | ||
| } | ||
| ''' | ||
| ]; | ||
|
|
||
| for (final RouteConfig def in _flatten()) { | ||
| items.add(def._extensionDefinition()); | ||
| } | ||
| @override | ||
| String get routeConstructorParameters => | ||
| navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'; | ||
|
|
||
| _enumDefinitions().forEach(items.add); | ||
| @override | ||
| String get routeDataClassName => 'ShellRouteData'; | ||
| } | ||
|
|
||
| yield* items; | ||
| /// The configuration to generate class declarations for a ShellRouteData. | ||
| class StatefulShellRouteConfig extends RouteBaseConfig { | ||
| StatefulShellRouteConfig._({ | ||
| required this.navigatorKey, | ||
| required super.routeDataClass, | ||
| required super.parent, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parent navigator key?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| required super.parentNavigatorKey, | ||
| }) : super._(); | ||
|
|
||
| yield* items | ||
| .expand( | ||
| (String e) => helperNames.entries | ||
| .where( | ||
| (MapEntry<String, String> element) => e.contains(element.key)) | ||
| .map((MapEntry<String, String> e) => e.value), | ||
| ) | ||
| .toSet(); | ||
| } | ||
| /// The command for calling the navigator key getter from the ShellRouteData. | ||
| final String? navigatorKey; | ||
|
|
||
| /// Returns `extension` code. | ||
| String _extensionDefinition() { | ||
| if (_typeName.contains('Shell')) { | ||
| return ''' | ||
| @override | ||
| Iterable<String> classDeclarations() => <String>[ | ||
| ''' | ||
| extension $_extensionName on $_className { | ||
| static $_className _fromState(GoRouterState state) => const $_className(); | ||
| } | ||
|
|
@@ -213,7 +89,7 @@ extension $_extensionName on $_className { | |
| navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'; | ||
|
||
|
|
||
| @override | ||
| String get routeDataClassName => 'ShellRouteData'; | ||
| String get routeDataClassName => 'StatefulShellRouteData'; | ||
| } | ||
|
|
||
| /// The configuration to generate class declarations for a GoRouteData. | ||
|
|
@@ -286,49 +162,13 @@ class GoRouteConfig extends RouteBaseConfig { | |
| buffer.writeln('const '); | ||
| } | ||
|
|
||
| return p.url.joinAll(pathSegments.reversed); | ||
| } | ||
|
|
||
| String get _className => _routeDataClass.name; | ||
|
|
||
| String get _extensionName => '\$${_className}Extension'; | ||
|
|
||
| String _routeDefinition() { | ||
| final String routesBit = _children.isEmpty | ||
| ? '' | ||
| : ''' | ||
| ${_typeName == 'TypedStatefulShellRoute' ? "branches:" : "routes:"} [${_children.map((RouteConfig e) => '${e._routeDefinition()},').join()}], | ||
| '''; | ||
| final String navigatorKeyParameterName = | ||
| _typeName.contains('Shell') ? 'navigatorKey' : 'parentNavigatorKey'; | ||
| final String navigatorKey = _key == null || _key!.isEmpty | ||
| ? '' | ||
| : '$navigatorKeyParameterName: $_key,'; | ||
| if (_typeName == 'TypedStatefulShellRoute') { | ||
| return ''' | ||
| StatefulShellRouteData.\$route( | ||
| factory: $_extensionName._fromState, | ||
| $navigatorKey | ||
| $routesBit | ||
| ) | ||
| '''; | ||
| } | ||
| if (_typeName == 'TypedStatefulShellBranch') { | ||
| return ''' | ||
| StatefulShellBranchData.\$branch( | ||
| $navigatorKey | ||
| $routesBit | ||
| ) | ||
| '''; | ||
| } | ||
| if (_typeName == 'TypedShellRoute') { | ||
| return ''' | ||
| ShellRouteData.\$route( | ||
| factory: $_extensionName._fromState, | ||
| $navigatorKey | ||
| $routesBit | ||
| ) | ||
| '''; | ||
| buffer.writeln('$_className('); | ||
| for (final ParameterElement param in <ParameterElement>[ | ||
| ..._ctorParams, | ||
| ..._ctorQueryParams, | ||
| if (_extraParam != null) _extraParam!, | ||
| ]) { | ||
| buffer.write(_decodeFor(param)); | ||
| } | ||
| buffer.writeln(');'); | ||
|
|
||
|
|
@@ -528,7 +368,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( | ||
|
|
@@ -545,7 +385,7 @@ abstract class RouteBaseConfig { | |
| final InterfaceElement classElement = typeParamType.element; | ||
|
|
||
| final RouteBaseConfig value; | ||
| if (isShellRoute) { | ||
| if (typeName =='TypedShellRoute') { | ||
| value = ShellRouteConfig._( | ||
| routeDataClass: classElement, | ||
| parent: parent, | ||
|
|
@@ -558,7 +398,22 @@ abstract class RouteBaseConfig { | |
| keyName: r'$parentNavigatorKey', | ||
| ), | ||
| ); | ||
| } else { | ||
| } | ||
| else if(typeName=='TypedStatefulShellRoute'){ | ||
| value = StatefulShellRouteConfig._( | ||
| 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( | ||
|
|
@@ -773,4 +628,4 @@ const String _enumConverterHelper = ''' | |
| extension<T extends Enum> on Map<T, String> { | ||
| T $enumExtensionHelperName(String value) => | ||
| entries.singleWhere((element) => element.value == value).key; | ||
| }'''; | ||
| }'''; | ||
Uh oh!
There was an error while loading. Please reload this page.