Skip to content
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

[go_router_builder] Adds support for required $extra parameter #3627

Merged
merged 11 commits into from
Apr 11, 2023
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 @@
## 1.2.1

* Supports opt-in required extra parameters. [#117261](https://github.com/flutter/flutter/issues/117261)

## 1.2.0

* Adds Support for ShellRoute
Expand Down
123 changes: 123 additions & 0 deletions packages/go_router_builder/example/lib/extra_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// 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:flutter/material.dart';
import 'package:go_router/go_router.dart';

part 'extra_example.g.dart';

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

final GoRouter _router = GoRouter(
routes: $appRoutes,
initialLocation: '/splash',
);

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

@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}

class Extra {
const Extra(this.value);

final int value;
}

@TypedGoRoute<RequiredExtraRoute>(path: '/requiredExtra')
class RequiredExtraRoute extends GoRouteData {
const RequiredExtraRoute({required this.$extra});

final Extra $extra;

@override
Widget build(BuildContext context, GoRouterState state) =>
RequiredExtraScreen(extra: $extra);
}

class RequiredExtraScreen extends StatelessWidget {
const RequiredExtraScreen({super.key, required this.extra});

final Extra extra;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Required Extra')),
body: Center(child: Text('Extra: ${extra.value}')),
);
}
}

@TypedGoRoute<OptionalExtraRoute>(path: '/optionalExtra')
class OptionalExtraRoute extends GoRouteData {
const OptionalExtraRoute({this.$extra});

final Extra? $extra;

@override
Widget build(BuildContext context, GoRouterState state) =>
OptionalExtraScreen(extra: $extra);
}

class OptionalExtraScreen extends StatelessWidget {
const OptionalExtraScreen({super.key, this.extra});

final Extra? extra;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Optional Extra')),
body: Center(child: Text('Extra: ${extra?.value}')),
);
}
}

@TypedGoRoute<SplashRoute>(path: '/splash')
class SplashRoute extends GoRouteData {
const SplashRoute();

@override
Widget build(BuildContext context, GoRouterState state) => const Splash();
}

class Splash extends StatelessWidget {
const Splash({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Splash')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Placeholder(),
ElevatedButton(
onPressed: () =>
const RequiredExtraRoute($extra: Extra(1)).go(context),
child: const Text('Required Extra'),
),
ElevatedButton(
onPressed: () =>
const OptionalExtraRoute($extra: Extra(2)).go(context),
child: const Text('Optional Extra'),
),
ElevatedButton(
onPressed: () => const OptionalExtraRoute().go(context),
child: const Text('Optional Extra (null)'),
),
],
),
);
}
}
81 changes: 81 additions & 0 deletions packages/go_router_builder/example/lib/extra_example.g.dart

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

15 changes: 6 additions & 9 deletions packages/go_router_builder/lib/src/route_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ RouteBase get $_routeGetterName => ${_routeDefinition()};

String get _newFromState {
final StringBuffer buffer = StringBuffer('=>');
if (_ctor.isConst && _ctorParams.isEmpty && _ctorQueryParams.isEmpty) {
if (_ctor.isConst &&
_ctorParams.isEmpty &&
_ctorQueryParams.isEmpty &&
_extraParam == null) {
buffer.writeln('const ');
}

Expand Down Expand Up @@ -367,7 +370,7 @@ GoRouteData.\$route(
);
}

if (!_pathParams.contains(element.name)) {
if (!_pathParams.contains(element.name) && !element.isExtraField) {
throw InvalidGenerationSourceError(
'Missing param `${element.name}` in path.',
element: element,
Expand Down Expand Up @@ -438,13 +441,7 @@ GoRouteData.\$route(

late final List<ParameterElement> _ctorParams =
_ctor.parameters.where((ParameterElement element) {
if (element.isRequired) {
if (element.isExtraField) {
throw InvalidGenerationSourceError(
'Parameters named `$extraFieldName` cannot be required.',
element: element,
);
}
if (element.isRequired && !element.isExtraField) {
return true;
}
return false;
Expand Down
8 changes: 4 additions & 4 deletions packages/go_router_builder/lib/src/type_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ String encodeField(PropertyAccessorElement element) {
String enumMapName(InterfaceType type) => '_\$${type.element.name}EnumMap';

String _stateValueAccess(ParameterElement element) {
if (element.isRequired) {
return 'params[${escapeDartString(element.name)}]!';
if (element.isExtraField) {
return 'extra as ${element.type.getDisplayString(withNullability: element.isOptional)}';
}

if (element.isExtraField) {
return 'extra as ${element.type.getDisplayString(withNullability: true)}';
if (element.isRequired) {
return 'params[${escapeDartString(element.name)}]!';
}

if (element.isOptional) {
Expand Down
2 changes: 1 addition & 1 deletion 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: 1.2.0
version: 1.2.1
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 Down
2 changes: 1 addition & 1 deletion packages/go_router_builder/test/builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Future<void> main() async {
const Set<String> _expectedAnnotatedTests = <String>{
'AppliedToWrongClassType',
'BadPathParam',
'ExtraMustBeOptional',
'ExtraValueRoute',
'RequiredExtraValueRoute',
'MissingPathParam',
'MissingPathValue',
'MissingTypeAnnotation',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,6 @@ class NullableRequiredParam extends GoRouteData {
final int? id;
}

@ShouldThrow(
r'Parameters named `$extra` cannot be required.',
)
@TypedGoRoute<ExtraMustBeOptional>(path: r'bob/:$extra')
class ExtraMustBeOptional extends GoRouteData {
ExtraMustBeOptional({required this.$extra});
final int $extra;
}

@ShouldThrow(
'Missing param `id` in path.',
)
Expand Down Expand Up @@ -204,6 +195,36 @@ class ExtraValueRoute extends GoRouteData {
final int? $extra;
}

@ShouldGenerate(r'''
RouteBase get $requiredExtraValueRoute => GoRouteData.$route(
path: '/default-value-route',
factory: $RequiredExtraValueRouteExtension._fromState,
);

extension $RequiredExtraValueRouteExtension on RequiredExtraValueRoute {
static RequiredExtraValueRoute _fromState(GoRouterState state) =>
RequiredExtraValueRoute(
$extra: state.extra as int,
);

String get location => GoRouteData.$location(
'/default-value-route',
);

void go(BuildContext context) => context.go(location, extra: $extra);

void push(BuildContext context) => context.push(location, extra: $extra);

void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: $extra);
}
''')
@TypedGoRoute<RequiredExtraValueRoute>(path: '/default-value-route')
class RequiredExtraValueRoute extends GoRouteData {
RequiredExtraValueRoute({required this.$extra});
final int $extra;
}

@ShouldThrow(
'Default value used with a nullable type. Only non-nullable type can have a default value.',
todo: 'Remove the default value or make the type non-nullable.',
Expand Down