Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/go_router_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.2.0

- Adds supports for `TypedGoRouteParameter` annotation.

## 4.1.3

* Requires `analyzer` 8.2 or higher, to avoid experimental APIs.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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.

// ignore_for_file: public_member_api_docs

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

part 'typed_go_route_parameter_example.g.dart';

void main() => runApp(App());

class App extends StatelessWidget {
App({super.key});

@override
Widget build(BuildContext context) =>
MaterialApp.router(routerConfig: _router);

final GoRouter _router = GoRouter(
initialLocation: '/int-route',
routes: $appRoutes,
);
}

@TypedGoRoute<IntRoute>(path: '/int-route')
class IntRoute extends GoRouteData with $IntRoute {
IntRoute({
@TypedGoRouteParameter(name: 'intField') this.intField,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be on the property declaration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was easier to put it in the parameter. But if you prefer, we can move it to the property instead. It will require a bit more code, though.

@TypedGoRouteParameter(name: 'int_field_with_default_value')
this.intFieldWithDefaultValue = 1,
});

final int? intField;
final int intFieldWithDefaultValue;

@override
Widget build(BuildContext context, GoRouterState state) => Screen(
intField: intField,
intFieldWithDefaultValue: intFieldWithDefaultValue,
);
}

class Screen extends StatelessWidget {
const Screen({
required this.intField,
required this.intFieldWithDefaultValue,
super.key,
});

final int? intField;
final int intFieldWithDefaultValue;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Go router with custom URI parameter names'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: const Text('intField:'),
subtitle: Text('$intField'),
trailing: const Icon(Icons.add),
onTap: () {
final int newValue = (intField ?? 0) + 1;
IntRoute(
intField: newValue,
intFieldWithDefaultValue: intFieldWithDefaultValue,
).go(context);
},
),
ListTile(
title: const Text('intFieldWithDefaultValue:'),
subtitle: Text('$intFieldWithDefaultValue'),
trailing: const Icon(Icons.add),
onTap: () {
final int newValue = intFieldWithDefaultValue + 1;
IntRoute(
intField: intField,
intFieldWithDefaultValue: newValue,
).go(context);
},
),
],
),
),
);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions packages/go_router_builder/lib/src/route_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ mixin _GoRouteMixin on RouteBaseConfig {

for (final FormalParameterElement param in _ctorQueryParams) {
final String parameterName = param.displayName;

final conditions = <String>[];
if (param.hasDefaultValue) {
if (param.type.isNullableType) {
Expand All @@ -335,7 +334,7 @@ mixin _GoRouteMixin on RouteBaseConfig {
line = 'if (${conditions.join(' && ')}) ';
}
line +=
'${escapeDartString(parameterName.kebab)}: '
'${param.uriName}: '
'${_encodeFor(parameterName)},';

buffer.writeln(line);
Expand Down
25 changes: 20 additions & 5 deletions packages/go_router_builder/lib/src/type_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ String _stateValueAccess(
if (pathParameters.contains(element.displayName)) {
access = 'pathParameters[${escapeDartString(element.displayName)}]$suffix';
} else {
access =
'uri.queryParameters[${escapeDartString(element.displayName.kebab)}]$suffix';
access = 'uri.queryParameters[${element.uriName}]$suffix';
}

return access;
Expand Down Expand Up @@ -635,11 +634,11 @@ class _TypeHelperIterable extends _TypeHelperWithHelper {

return '''
state.uri.queryParametersAll[
${escapeDartString(parameterElement.displayName.kebab)}]
${parameterElement.uriName}]
?.map($entriesTypeDecoder)$convertToNotNull$iterableCaster$fallBack''';
}
return '''
state.uri.queryParametersAll[${escapeDartString(parameterElement.displayName.kebab)}]''';
state.uri.queryParametersAll[${parameterElement.uriName}]''';
}

@override
Expand Down Expand Up @@ -822,7 +821,7 @@ abstract class _TypeHelperWithHelper extends _TypeHelper {
if (!pathParameters.contains(parameterName) &&
(paramType.isNullableType || parameterElement.hasDefaultValue)) {
return '$convertMapValueHelperName('
'${escapeDartString(parameterName.kebab)}, '
'${parameterElement.uriName}, '
'state.uri.queryParameters, '
'${helperName(paramType)})';
}
Expand Down Expand Up @@ -853,6 +852,18 @@ extension FormalParameterElementExtension on FormalParameterElement {

/// Returns `true` if `this` has a name that matches [extraFieldName];
bool get isExtraField => displayName == extraFieldName;

/// Returns the URI name for this parameter, taking into account any
/// `TypedGoRouteParameter` annotation that may override the name.
String get uriName {
final typedGoRouteParameterReader = ConstantReader(
_typedGoRouteParameterChecker.firstAnnotationOf(this),
);
final String name =
typedGoRouteParameterReader.peek('name')?.stringValue ??
displayName.kebab;
return escapeDartString(name);
}
}

/// An error thrown when a default value is used with a nullable type.
Expand All @@ -865,3 +876,7 @@ class NullableDefaultValueError extends InvalidGenerationSourceError {
element: element,
);
}

const _typedGoRouteParameterChecker = TypeChecker.fromUrl(
'package:go_router/src/route_data.dart#TypedGoRouteParameter',
);
4 changes: 2 additions & 2 deletions packages/go_router_builder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: go_router_builder
description: >-
A builder that supports generated strongly-typed route helpers for
package:go_router
version: 4.1.3
version: 4.2.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

Expand All @@ -27,7 +27,7 @@ dev_dependencies:
dart_style: ">=2.3.7 <4.0.0"
flutter:
sdk: flutter
go_router: ^16.2.0
go_router: ^17.1.0
leak_tracker_flutter_testing: ">=3.0.0"
package_config: ^2.1.1
pub_semver: ^2.1.5
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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:go_router/go_router.dart';

mixin $OverriddenParameterNameRoute {}

@TypedGoRoute<OverriddenParameterNameRoute>(path: '/typed-go-route-parameter')
class OverriddenParameterNameRoute extends GoRouteData
with $OverriddenParameterNameRoute {
OverriddenParameterNameRoute({
@TypedGoRouteParameter(name: 'parameterNameOverride') this.withAnnotation,
});
final int? withAnnotation;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
RouteBase get $overriddenParameterNameRoute => GoRouteData.$route(
path: '/typed-go-route-parameter',
factory: $OverriddenParameterNameRoute._fromState,
);

mixin $OverriddenParameterNameRoute on GoRouteData {
static OverriddenParameterNameRoute _fromState(GoRouterState state) =>
OverriddenParameterNameRoute(
withAnnotation: _$convertMapValue(
'parameterNameOverride',
state.uri.queryParameters,
int.tryParse,
),
);

OverriddenParameterNameRoute get _self =>
this as OverriddenParameterNameRoute;

@override
String get location => GoRouteData.$location(
'/typed-go-route-parameter',
queryParams: {
if (_self.withAnnotation != null)
'parameterNameOverride': _self.withAnnotation!.toString(),
},
);

@override
void go(BuildContext context) => context.go(location);

@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);

@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);

@override
void replace(BuildContext context) => context.replace(location);
}

T? _$convertMapValue<T>(
String key,
Map<String, String> map,
T? Function(String) converter,
) {
final value = map[key];
return value == null ? null : converter(value);
}
Loading