Skip to content
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
7 changes: 7 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 12.0.0

- Adds ability to dynamically update routing table.
- **BREAKING CHANGE**:
- The function signature of constructor of `RouteConfiguration` is updated.
- Adds a required `matchedPath` named parameter to `RouteMatch.match`.

## 11.1.4

- Fixes missing parameters in the type-safe routes topic documentation.
Expand Down
1 change: 1 addition & 0 deletions packages/go_router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ See the API documentation for details on the following topics:
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)

## Migration Guides
- [Migrating to 12.0.0](https://flutter.dev/go/go-router-v12-breaking-changes).
- [Migrating to 11.0.0](https://flutter.dev/go/go-router-v11-breaking-changes).
- [Migrating to 10.0.0](https://flutter.dev/go/go-router-v10-breaking-changes).
- [Migrating to 9.0.0](https://flutter.dev/go/go-router-v9-breaking-changes).
Expand Down
29 changes: 29 additions & 0 deletions packages/go_router/doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,35 @@ GoRoute(
)
```

# Dynamic RoutingConfig
The [RoutingConfig][] provides a way to update the GoRoute\[s\] after
the [GoRouter][] has already created. This can be done by creating a GoRouter
with special constructor [GoRouter.routingConfig][]

```dart
final ValueNotifier<RoutingConfig> myRoutingConfig = ValueNotifier<RoutingConfig>(
RoutingConfig(
routes: <RouteBase>[GoRoute(path: '/', builder: (_, __) => HomeScreen())],
),
);
final GoRouter router = GoRouter.routingConfig(routingConfig: myRoutingConfig);
```

To change the GoRoute later, modify the value of the [ValueNotifier][] directly.

```dart
myRoutingConfig.value = RoutingConfig(
routes: <RouteBase>[
GoRoute(path: '/', builder: (_, __) => AlternativeHomeScreen()),
GoRoute(path: '/a-new-route', builder: (_, __) => SomeScreen()),
],
);
```

The value change is automatically picked up by GoRouter and causes it to reparse
the current routes, i.e. RouteMatchList, stored in GoRouter. The RouteMatchList will
reflect the latest change of the `RoutingConfig`.

# Nested navigation
Some apps display destinations in a subsection of the screen, for example, an
app using a BottomNavigationBar that stays on-screen when navigating between
Expand Down
108 changes: 108 additions & 0 deletions packages/go_router/example/lib/routing_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// 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.

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

/// This app shows how to dynamically add more route into routing config
void main() => runApp(const MyApp());

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

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
bool isNewRouteAdded = false;

late final ValueNotifier<RoutingConfig> myConfig =
ValueNotifier<RoutingConfig>(_generateRoutingConfig());

late final GoRouter router = GoRouter.routingConfig(
routingConfig: myConfig,
errorBuilder: (_, GoRouterState state) => Scaffold(
appBar: AppBar(title: const Text('Page not found')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('${state.uri} does not exist'),
ElevatedButton(
onPressed: () => router.go('/'),
child: const Text('Go to home')),
],
)),
));

RoutingConfig _generateRoutingConfig() {
return RoutingConfig(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (_, __) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: isNewRouteAdded
? null
: () {
setState(() {
isNewRouteAdded = true;
// Modify the routing config.
myConfig.value = _generateRoutingConfig();
});
},
child: isNewRouteAdded
? const Text('A route has been added')
: const Text('Add a new route'),
),
ElevatedButton(
onPressed: () {
router.go('/new-route');
},
child: const Text('Try going to /new-route'),
)
],
),
),
);
},
),
if (isNewRouteAdded)
GoRoute(
path: '/new-route',
builder: (_, __) {
return Scaffold(
appBar: AppBar(title: const Text('A new Route')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => router.go('/'),
child: const Text('Go to home')),
],
)),
);
},
),
],
);
}

@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
);
}
}
31 changes: 31 additions & 0 deletions packages/go_router/example/test/routing_config_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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.

import 'package:flutter_test/flutter_test.dart';
import 'package:go_router_examples/routing_config.dart' as example;

void main() {
testWidgets('example works', (WidgetTester tester) async {
await tester.pumpWidget(const example.MyApp());
expect(find.text('Add a new route'), findsOneWidget);

await tester.tap(find.text('Try going to /new-route'));
await tester.pumpAndSettle();

expect(find.text('Page not found'), findsOneWidget);

await tester.tap(find.text('Go to home'));
await tester.pumpAndSettle();

await tester.tap(find.text('Add a new route'));
await tester.pumpAndSettle();

expect(find.text('A route has been added'), findsOneWidget);

await tester.tap(find.text('Try going to /new-route'));
await tester.pumpAndSettle();

expect(find.text('A new Route'), findsOneWidget);
});
}
Loading