Skip to content

Commit 1a24dad

Browse files
authored
[go_router] adds ability to dynamically update routing table. (flutter#5079)
fixes flutter#136005
1 parent 47a14fd commit 1a24dad

18 files changed

+711
-227
lines changed

packages/go_router/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 12.0.0
2+
3+
- Adds ability to dynamically update routing table.
4+
- **BREAKING CHANGE**:
5+
- The function signature of constructor of `RouteConfiguration` is updated.
6+
- Adds a required `matchedPath` named parameter to `RouteMatch.match`.
7+
18
## 11.1.4
29

310
- Fixes missing parameters in the type-safe routes topic documentation.

packages/go_router/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ See the API documentation for details on the following topics:
3737
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)
3838

3939
## Migration Guides
40+
- [Migrating to 12.0.0](https://flutter.dev/go/go-router-v12-breaking-changes).
4041
- [Migrating to 11.0.0](https://flutter.dev/go/go-router-v11-breaking-changes).
4142
- [Migrating to 10.0.0](https://flutter.dev/go/go-router-v10-breaking-changes).
4243
- [Migrating to 9.0.0](https://flutter.dev/go/go-router-v9-breaking-changes).

packages/go_router/doc/configuration.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,35 @@ GoRoute(
8585
)
8686
```
8787

88+
# Dynamic RoutingConfig
89+
The [RoutingConfig][] provides a way to update the GoRoute\[s\] after
90+
the [GoRouter][] has already created. This can be done by creating a GoRouter
91+
with special constructor [GoRouter.routingConfig][]
92+
93+
```dart
94+
final ValueNotifier<RoutingConfig> myRoutingConfig = ValueNotifier<RoutingConfig>(
95+
RoutingConfig(
96+
routes: <RouteBase>[GoRoute(path: '/', builder: (_, __) => HomeScreen())],
97+
),
98+
);
99+
final GoRouter router = GoRouter.routingConfig(routingConfig: myRoutingConfig);
100+
```
101+
102+
To change the GoRoute later, modify the value of the [ValueNotifier][] directly.
103+
104+
```dart
105+
myRoutingConfig.value = RoutingConfig(
106+
routes: <RouteBase>[
107+
GoRoute(path: '/', builder: (_, __) => AlternativeHomeScreen()),
108+
GoRoute(path: '/a-new-route', builder: (_, __) => SomeScreen()),
109+
],
110+
);
111+
```
112+
113+
The value change is automatically picked up by GoRouter and causes it to reparse
114+
the current routes, i.e. RouteMatchList, stored in GoRouter. The RouteMatchList will
115+
reflect the latest change of the `RoutingConfig`.
116+
88117
# Nested navigation
89118
Some apps display destinations in a subsection of the screen, for example, an
90119
app using a BottomNavigationBar that stays on-screen when navigating between
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:go_router/go_router.dart';
7+
8+
/// This app shows how to dynamically add more route into routing config
9+
void main() => runApp(const MyApp());
10+
11+
/// The main app.
12+
class MyApp extends StatefulWidget {
13+
/// Constructs a [MyApp]
14+
const MyApp({super.key});
15+
16+
@override
17+
State<MyApp> createState() => _MyAppState();
18+
}
19+
20+
class _MyAppState extends State<MyApp> {
21+
bool isNewRouteAdded = false;
22+
23+
late final ValueNotifier<RoutingConfig> myConfig =
24+
ValueNotifier<RoutingConfig>(_generateRoutingConfig());
25+
26+
late final GoRouter router = GoRouter.routingConfig(
27+
routingConfig: myConfig,
28+
errorBuilder: (_, GoRouterState state) => Scaffold(
29+
appBar: AppBar(title: const Text('Page not found')),
30+
body: Center(
31+
child: Column(
32+
mainAxisAlignment: MainAxisAlignment.center,
33+
children: <Widget>[
34+
Text('${state.uri} does not exist'),
35+
ElevatedButton(
36+
onPressed: () => router.go('/'),
37+
child: const Text('Go to home')),
38+
],
39+
)),
40+
));
41+
42+
RoutingConfig _generateRoutingConfig() {
43+
return RoutingConfig(
44+
routes: <RouteBase>[
45+
GoRoute(
46+
path: '/',
47+
builder: (_, __) {
48+
return Scaffold(
49+
appBar: AppBar(title: const Text('Home')),
50+
body: Center(
51+
child: Row(
52+
mainAxisAlignment: MainAxisAlignment.center,
53+
children: <Widget>[
54+
ElevatedButton(
55+
onPressed: isNewRouteAdded
56+
? null
57+
: () {
58+
setState(() {
59+
isNewRouteAdded = true;
60+
// Modify the routing config.
61+
myConfig.value = _generateRoutingConfig();
62+
});
63+
},
64+
child: isNewRouteAdded
65+
? const Text('A route has been added')
66+
: const Text('Add a new route'),
67+
),
68+
ElevatedButton(
69+
onPressed: () {
70+
router.go('/new-route');
71+
},
72+
child: const Text('Try going to /new-route'),
73+
)
74+
],
75+
),
76+
),
77+
);
78+
},
79+
),
80+
if (isNewRouteAdded)
81+
GoRoute(
82+
path: '/new-route',
83+
builder: (_, __) {
84+
return Scaffold(
85+
appBar: AppBar(title: const Text('A new Route')),
86+
body: Center(
87+
child: Column(
88+
mainAxisAlignment: MainAxisAlignment.center,
89+
children: <Widget>[
90+
ElevatedButton(
91+
onPressed: () => router.go('/'),
92+
child: const Text('Go to home')),
93+
],
94+
)),
95+
);
96+
},
97+
),
98+
],
99+
);
100+
}
101+
102+
@override
103+
Widget build(BuildContext context) {
104+
return MaterialApp.router(
105+
routerConfig: router,
106+
);
107+
}
108+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:go_router_examples/routing_config.dart' as example;
7+
8+
void main() {
9+
testWidgets('example works', (WidgetTester tester) async {
10+
await tester.pumpWidget(const example.MyApp());
11+
expect(find.text('Add a new route'), findsOneWidget);
12+
13+
await tester.tap(find.text('Try going to /new-route'));
14+
await tester.pumpAndSettle();
15+
16+
expect(find.text('Page not found'), findsOneWidget);
17+
18+
await tester.tap(find.text('Go to home'));
19+
await tester.pumpAndSettle();
20+
21+
await tester.tap(find.text('Add a new route'));
22+
await tester.pumpAndSettle();
23+
24+
expect(find.text('A route has been added'), findsOneWidget);
25+
26+
await tester.tap(find.text('Try going to /new-route'));
27+
await tester.pumpAndSettle();
28+
29+
expect(find.text('A new Route'), findsOneWidget);
30+
});
31+
}

0 commit comments

Comments
 (0)