Skip to content

Commit

Permalink
[go_router_builder] Adds support for required $extra parameter (flutt…
Browse files Browse the repository at this point in the history
…er#3627)

[go_router_builder] Adds support for required $extra parameter
  • Loading branch information
dancamdev authored Apr 11, 2023
1 parent 02ffd94 commit 1ddd2d9
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 24 deletions.
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

0 comments on commit 1ddd2d9

Please sign in to comment.