Skip to content

Commit f408578

Browse files
authored
[flutter_adaptive_scaffold] Go router sample for AdaptiveScaffold (flutter#7452)
This implements a sample of using GoRouter with AdaptiveScaffold. It also helps testing advanced adaptive scenarios. *List which issues are fixed by this PR. You must list at least one issue.* flutter#129850
1 parent 6e26197 commit f408578

20 files changed

+731
-2
lines changed

packages/flutter_adaptive_scaffold/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.6
2+
3+
* Add new sample for using AdaptiveScaffold with GoRouter.
4+
15
## 0.2.5
26

37
* Fix breakpoint not being active in certain cases like foldables.

packages/flutter_adaptive_scaffold/example/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Examples
2+
23
There are several examples listed in this directory:
34
You can run the following commands in the example directory to see the appropriate demos:
45

@@ -7,3 +8,5 @@ You can run the following commands in the example directory to see the appropria
78
`flutter run lib/adaptive_layout_demo.dart` to see a simple usage of AdaptiveLayout.
89

910
`flutter run lib/adaptive_scaffold_demo.dart` to see a simple usage of AdaptiveScaffold.
11+
12+
`flutter run lib/go_router_demo.dart` to see usage of AdaptiveScaffold with GoRouter and some advanced scenarios like auth handling and branches.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
7+
import 'go_router_demo/app_router.dart';
8+
9+
void main() {
10+
runApp(const MyApp());
11+
}
12+
13+
/// The main application widget for this example.
14+
class MyApp extends StatelessWidget {
15+
/// Creates a const main application widget.
16+
const MyApp({super.key});
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return MaterialApp.router(
21+
restorationScopeId: 'demo',
22+
routerConfig: AppRouter.router,
23+
theme: ThemeData(
24+
colorScheme: ColorScheme.fromSeed(
25+
seedColor: Colors.blue,
26+
dynamicSchemeVariant: DynamicSchemeVariant.vibrant,
27+
primary: Colors.blue,
28+
),
29+
useMaterial3: true,
30+
),
31+
);
32+
}
33+
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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+
import 'pages/pages.dart';
9+
import 'scaffold_shell.dart';
10+
11+
/// The root navigator key for the main router of the app.
12+
final GlobalKey<NavigatorState> rootNavigatorKey =
13+
GlobalKey<NavigatorState>(debugLabel: 'root');
14+
15+
final GlobalKey<NavigatorState> _homeNavigatorKey =
16+
GlobalKey<NavigatorState>(debugLabel: 'home');
17+
final GlobalKey<NavigatorState> _counterNavigatorKey =
18+
GlobalKey<NavigatorState>(debugLabel: 'counter');
19+
final GlobalKey<NavigatorState> _moreNavigatorKey =
20+
GlobalKey<NavigatorState>(debugLabel: 'more');
21+
22+
/// The [AppRouter] maintains the main route configuration for the app.
23+
///
24+
/// Routes that are `fullScreenDialogs` should also set `_rootNavigatorKey` as
25+
/// the `parentNavigatorKey` to ensure that the dialog is displayed correctly.
26+
class AppRouter {
27+
/// The authentication status of the user.
28+
static ValueNotifier<bool> authenticatedNotifier = ValueNotifier<bool>(false);
29+
30+
/// The router with the routes of pages that should be displayed.
31+
static final GoRouter router = GoRouter(
32+
navigatorKey: rootNavigatorKey,
33+
debugLogDiagnostics: true,
34+
errorPageBuilder: (BuildContext context, GoRouterState state) {
35+
return const MaterialPage<void>(child: NavigationErrorPage());
36+
},
37+
redirect: (BuildContext context, GoRouterState state) {
38+
if (state.uri.path == '/') {
39+
return HomePage.path;
40+
}
41+
return null;
42+
},
43+
refreshListenable: authenticatedNotifier,
44+
routes: <RouteBase>[
45+
_unauthenticatedRoutes,
46+
_authenticatedRoutes,
47+
..._openRoutes,
48+
],
49+
);
50+
51+
static final GoRoute _unauthenticatedRoutes = GoRoute(
52+
name: LoginPage.name,
53+
path: LoginPage.path,
54+
pageBuilder: (BuildContext context, GoRouterState state) {
55+
return const MaterialPage<void>(child: LoginPage());
56+
},
57+
redirect: (BuildContext context, GoRouterState state) {
58+
if (authenticatedNotifier.value) {
59+
return HomePage.path;
60+
}
61+
return null;
62+
},
63+
routes: <RouteBase>[
64+
GoRoute(
65+
name: ForgotPasswordPage.name,
66+
path: ForgotPasswordPage.path,
67+
pageBuilder: (BuildContext context, GoRouterState state) {
68+
return const MaterialPage<void>(
69+
child: ForgotPasswordPage(),
70+
);
71+
},
72+
),
73+
],
74+
);
75+
76+
static final StatefulShellRoute _authenticatedRoutes =
77+
StatefulShellRoute.indexedStack(
78+
parentNavigatorKey: rootNavigatorKey,
79+
builder: (
80+
BuildContext context,
81+
GoRouterState state,
82+
StatefulNavigationShell navigationShell,
83+
) {
84+
return ScaffoldShell(navigationShell: navigationShell);
85+
},
86+
redirect: (BuildContext context, GoRouterState state) {
87+
if (!authenticatedNotifier.value) {
88+
return LoginPage.path;
89+
}
90+
return null;
91+
},
92+
branches: <StatefulShellBranch>[
93+
StatefulShellBranch(
94+
navigatorKey: _homeNavigatorKey,
95+
routes: <RouteBase>[
96+
GoRoute(
97+
name: HomePage.name,
98+
path: HomePage.path,
99+
pageBuilder: (BuildContext context, GoRouterState state) {
100+
return const NoTransitionPage<void>(
101+
child: HomePage(),
102+
);
103+
},
104+
routes: <RouteBase>[
105+
GoRoute(
106+
name: DetailOverviewPage.name,
107+
path: DetailOverviewPage.path,
108+
pageBuilder: (BuildContext context, GoRouterState state) {
109+
return const MaterialPage<void>(
110+
child: DetailOverviewPage(),
111+
);
112+
},
113+
routes: <RouteBase>[
114+
GoRoute(
115+
name: DetailPage.name,
116+
path: DetailPage.path,
117+
pageBuilder: (BuildContext context, GoRouterState state) {
118+
return MaterialPage<void>(
119+
child: DetailPage(
120+
itemName: state.uri.queryParameters['itemName']!),
121+
);
122+
},
123+
),
124+
]),
125+
GoRoute(
126+
name: DetailModalPage.name,
127+
path: DetailModalPage.path,
128+
parentNavigatorKey: rootNavigatorKey,
129+
pageBuilder: (BuildContext context, GoRouterState state) {
130+
return const MaterialPage<void>(
131+
fullscreenDialog: true,
132+
child: DetailModalPage(),
133+
);
134+
},
135+
),
136+
],
137+
),
138+
],
139+
),
140+
StatefulShellBranch(
141+
navigatorKey: _counterNavigatorKey,
142+
routes: <RouteBase>[
143+
GoRoute(
144+
name: CounterPage.name,
145+
path: CounterPage.path,
146+
pageBuilder: (BuildContext context, GoRouterState state) {
147+
return const NoTransitionPage<void>(child: CounterPage());
148+
},
149+
),
150+
],
151+
),
152+
StatefulShellBranch(
153+
navigatorKey: _moreNavigatorKey,
154+
routes: <RouteBase>[
155+
GoRoute(
156+
name: MorePage.name,
157+
path: MorePage.path,
158+
pageBuilder: (BuildContext context, GoRouterState state) {
159+
return const NoTransitionPage<void>(
160+
key: ValueKey<String>(MorePage.name),
161+
child: MorePage(),
162+
);
163+
},
164+
routes: <RouteBase>[
165+
GoRoute(
166+
path: ProfilePage.path,
167+
name: ProfilePage.name,
168+
pageBuilder: (BuildContext context, GoRouterState state) {
169+
return const MaterialPage<void>(child: ProfilePage());
170+
},
171+
),
172+
GoRoute(
173+
name: SettingsPage.name,
174+
path: SettingsPage.path,
175+
pageBuilder: (BuildContext context, GoRouterState state) {
176+
return const MaterialPage<void>(child: SettingsPage());
177+
},
178+
),
179+
],
180+
),
181+
],
182+
),
183+
],
184+
);
185+
186+
static final List<GoRoute> _openRoutes = <GoRoute>[
187+
GoRoute(
188+
name: LanguagePage.name,
189+
path: LanguagePage.path,
190+
pageBuilder: (BuildContext context, GoRouterState state) {
191+
return const MaterialPage<void>(
192+
child: LanguagePage(),
193+
);
194+
},
195+
),
196+
];
197+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
7+
/// The counter page.
8+
class CounterPage extends StatelessWidget {
9+
/// Construct the counter page.
10+
const CounterPage({super.key});
11+
12+
/// The path for the counter page.
13+
static const String path = '/counter';
14+
15+
/// The name for the counter page.
16+
static const String name = 'Counter';
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return Scaffold(
21+
appBar: AppBar(
22+
title: const Text('Counter Page'),
23+
),
24+
body: const Center(
25+
child: Text('Counter Page'),
26+
),
27+
);
28+
}
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
7+
/// The detail modal page.
8+
class DetailModalPage extends StatelessWidget {
9+
/// Construct the detail modal page.
10+
const DetailModalPage({super.key});
11+
12+
/// The path for the detail modal page.
13+
static const String path = 'detail-modal';
14+
15+
/// The name for the detail modal page.
16+
static const String name = 'DetailModal';
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return Scaffold(
21+
appBar: AppBar(
22+
title: const Text('Detail Modal Page'),
23+
),
24+
body: const Center(
25+
child: Text('Detail modal Page'),
26+
),
27+
);
28+
}
29+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
import 'detail_page.dart';
9+
10+
/// The detail overview page.
11+
class DetailOverviewPage extends StatelessWidget {
12+
/// Construct the detail overview page.
13+
const DetailOverviewPage({super.key});
14+
15+
/// The path for the detail page.
16+
static const String path = 'detail-overview';
17+
18+
/// The name for the detail page.
19+
static const String name = 'DetailOverview';
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
return Scaffold(
24+
appBar: AppBar(
25+
title: const Text('Detail Overview Page'),
26+
),
27+
body: ListView.builder(
28+
itemCount: 10,
29+
itemBuilder: (BuildContext context, int index) {
30+
return ListTile(
31+
title: Text('Item $index'),
32+
onTap: () {
33+
context.goNamed(
34+
DetailPage.name,
35+
queryParameters: <String, String>{'itemName': '$index'},
36+
);
37+
},
38+
);
39+
},
40+
),
41+
);
42+
}
43+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
7+
/// The detail page.
8+
class DetailPage extends StatelessWidget {
9+
/// Construct the detail page.
10+
const DetailPage({super.key, required this.itemName});
11+
12+
/// The path for the detail page.
13+
static const String path = 'detail';
14+
15+
/// The name for the detail page.
16+
static const String name = 'Detail';
17+
18+
/// The item name for the detail page.
19+
final String itemName;
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
return Scaffold(
24+
appBar: AppBar(
25+
title: const Text('Detail Page'),
26+
),
27+
body: Center(
28+
child: Text('Detail Page: $itemName'),
29+
),
30+
);
31+
}
32+
}

0 commit comments

Comments
 (0)