Skip to content

[go_router_builder]: Add support for relative routes #9749

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

Merged
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 @@
## 3.3.0

- Adds support for `TypedRelativeGoRoute`.

## 3.2.1

- Changes generated whitespace for better compatibility with new Dart formatter.
Expand Down
30 changes: 28 additions & 2 deletions packages/go_router_builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: ^16.0.0
go_router: ^16.2.0

dev_dependencies:
# ...along with your other dev-dependencies
build_runner: ^2.6.0
go_router_builder: ^3.1.0
go_router_builder: ^3.3.0
```

### Source code
Expand Down Expand Up @@ -455,6 +455,32 @@ class MyGoRouteData extends GoRouteData with _$MyGoRouteData {

An example is available [here](https://github.com/flutter/packages/blob/main/packages/go_router_builder/example/lib/shell_route_with_keys_example.dart).

## Relative routes

Relative routes allow reusing the same `RouteData` in different parts of the route tree.
Define a relative route by extending `RelativeGoRouteData`.

<?code-excerpt "example/lib/readme_excerpts.dart (relativeRoute)"?>
```dart
@TypedRelativeGoRoute<DetailsRoute>(path: 'details')
class DetailsRoute extends RelativeGoRouteData with _$DetailsRoute {
const DetailsRoute();

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

Navigate using the `goRelative` or `pushRelative` methods provided by the code generator:

<?code-excerpt "example/lib/readme_excerpts.dart (goRelative)"?>
```dart
void onTapRelative() => const DetailsRoute().goRelative(context);
```

Relative routing methods are not idempotent and will cause an error when the relative location does not match a route.

## Run tests

To run unit tests, run command `dart tool/run_tests.dart` from `packages/go_router_builder/`.
Expand Down
187 changes: 187 additions & 0 deletions packages/go_router_builder/example/lib/go_relative.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// 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, unreachable_from_main

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

part 'go_relative.g.dart';

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

/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({super.key});

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

/// The route configuration.
final GoRouter _router = GoRouter(routes: $appRoutes);
const TypedRelativeGoRoute<DetailsRoute> detailRoute =
TypedRelativeGoRoute<DetailsRoute>(
path: 'details/:detailId',
routes: <TypedRoute<RouteData>>[
TypedRelativeGoRoute<SettingsRoute>(path: 'settings/:settingId'),
],
);

@TypedGoRoute<HomeRoute>(
path: '/',
routes: <TypedRoute<RouteData>>[
TypedGoRoute<DashboardRoute>(
path: '/dashboard',
routes: <TypedRoute<RouteData>>[detailRoute],
),
detailRoute,
],
)
class HomeRoute extends GoRouteData with _$HomeRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const HomeScreen();
}
}

class DashboardRoute extends GoRouteData with _$DashboardRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const DashboardScreen();
}
}

class DetailsRoute extends RelativeGoRouteData with _$DetailsRoute {
const DetailsRoute({required this.detailId});
final String detailId;

@override
Widget build(BuildContext context, GoRouterState state) {
return DetailsScreen(id: detailId);
}
}

class SettingsRoute extends RelativeGoRouteData with _$SettingsRoute {
const SettingsRoute({required this.settingId});
final String settingId;

@override
Widget build(BuildContext context, GoRouterState state) {
return SettingsScreen(id: settingId);
}
}

/// The home screen
class HomeScreen extends StatelessWidget {
/// Constructs a [HomeScreen]
const HomeScreen({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
const DetailsRoute(detailId: 'DetailsId').goRelative(context);
},
child: const Text('Go to the Details screen'),
),
ElevatedButton(
onPressed: () {
DashboardRoute().go(context);
},
child: const Text('Go to the Dashboard screen'),
),
],
),
);
}
}

/// The home screen
class DashboardScreen extends StatelessWidget {
/// Constructs a [DashboardScreen]
const DashboardScreen({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dashboard Screen')),
body: Column(
children: <Widget>[
ElevatedButton(
onPressed: () {
const DetailsRoute(detailId: 'DetailsId').goRelative(context);
},
child: const Text('Go to the Details screen'),
),
ElevatedButton(
onPressed: () => context.pop(),
child: const Text('Go back'),
),
],
),
);
}
}

/// The details screen
class DetailsScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const DetailsScreen({super.key, required this.id});

final String id;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details Screen $id')),
body: Center(
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: () => context.pop(),
child: const Text('Go back'),
),
ElevatedButton(
onPressed:
() => const SettingsRoute(
settingId: 'SettingsId',
).goRelative(context),
child: const Text('Go to the Settings screen'),
),
],
),
),
);
}
}

/// The details screen
class SettingsScreen extends StatelessWidget {
/// Constructs a [SettingsScreen]
const SettingsScreen({super.key, required this.id});

final String id;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Settings Screen $id')),
body: Center(
child: TextButton(
onPressed: () => context.pop(),
child: const Text('Go back'),
),
),
);
}
}
148 changes: 148 additions & 0 deletions packages/go_router_builder/example/lib/go_relative.g.dart

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

Loading