mirror of
https://github.com/flutter/samples.git
synced 2025-11-11 15:28:44 +00:00
Update navigation_and_routing sample to go_router (#2067)
This is a new PR to update the navigation_and_routing sample to the latest version of go_router. It is a based on https://github.com/flutter/samples/pull/1437 --------- Co-authored-by: Brett Morgan <brettmorgan@google.com>
This commit is contained in:
@@ -3,10 +3,22 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'auth.dart';
|
||||
import 'routing.dart';
|
||||
import 'screens/navigator.dart';
|
||||
import 'data.dart';
|
||||
import 'screens/author_details.dart';
|
||||
import 'screens/authors.dart';
|
||||
import 'screens/book_details.dart';
|
||||
import 'screens/books.dart';
|
||||
import 'screens/scaffold.dart';
|
||||
import 'screens/settings.dart';
|
||||
import 'screens/sign_in.dart';
|
||||
import 'widgets/book_list.dart';
|
||||
import 'widgets/fade_transition_page.dart';
|
||||
|
||||
final appShellNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'app shell');
|
||||
final booksNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'books shell');
|
||||
|
||||
class Bookstore extends StatefulWidget {
|
||||
const Bookstore({super.key});
|
||||
@@ -16,98 +28,245 @@ class Bookstore extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _BookstoreState extends State<Bookstore> {
|
||||
final _auth = BookstoreAuth();
|
||||
final _navigatorKey = GlobalKey<NavigatorState>();
|
||||
late final RouteState _routeState;
|
||||
late final SimpleRouterDelegate _routerDelegate;
|
||||
late final TemplateRouteParser _routeParser;
|
||||
final BookstoreAuth auth = BookstoreAuth();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
/// Configure the parser with all of the app's allowed path templates.
|
||||
_routeParser = TemplateRouteParser(
|
||||
allowedPaths: [
|
||||
'/signin',
|
||||
'/authors',
|
||||
'/settings',
|
||||
'/books/new',
|
||||
'/books/all',
|
||||
'/books/popular',
|
||||
'/book/:bookId',
|
||||
'/author/:authorId',
|
||||
],
|
||||
guard: _guard,
|
||||
initialRoute: '/signin',
|
||||
);
|
||||
|
||||
_routeState = RouteState(_routeParser);
|
||||
|
||||
_routerDelegate = SimpleRouterDelegate(
|
||||
routeState: _routeState,
|
||||
navigatorKey: _navigatorKey,
|
||||
builder: (context) => BookstoreNavigator(
|
||||
navigatorKey: _navigatorKey,
|
||||
),
|
||||
);
|
||||
|
||||
// Listen for when the user logs out and display the signin screen.
|
||||
_auth.addListener(_handleAuthStateChanged);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => RouteStateScope(
|
||||
notifier: _routeState,
|
||||
child: BookstoreAuthScope(
|
||||
notifier: _auth,
|
||||
child: MaterialApp.router(
|
||||
routerDelegate: _routerDelegate,
|
||||
routeInformationParser: _routeParser,
|
||||
// Revert back to pre-Flutter-2.5 transition behavior:
|
||||
// https://github.com/flutter/flutter/issues/82053
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: const PageTransitionsTheme(
|
||||
builders: {
|
||||
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(),
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
builder: (context, child) {
|
||||
if (child == null) {
|
||||
throw ('No child in .router constructor builder');
|
||||
}
|
||||
return BookstoreAuthScope(
|
||||
notifier: auth,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
routerConfig: GoRouter(
|
||||
refreshListenable: auth,
|
||||
debugLogDiagnostics: true,
|
||||
initialLocation: '/books/popular',
|
||||
redirect: (context, state) {
|
||||
final signedIn = BookstoreAuth.of(context).signedIn;
|
||||
if (state.uri.toString() != '/sign-in' && !signedIn) {
|
||||
return '/sign-in';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
ShellRoute(
|
||||
navigatorKey: appShellNavigatorKey,
|
||||
builder: (context, state, child) {
|
||||
return BookstoreScaffold(
|
||||
selectedIndex: switch (state.uri.path) {
|
||||
var p when p.startsWith('/books') => 0,
|
||||
var p when p.startsWith('/authors') => 1,
|
||||
var p when p.startsWith('/settings') => 2,
|
||||
_ => 0,
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
ShellRoute(
|
||||
pageBuilder: (context, state, child) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
key: state.pageKey,
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
child: Builder(builder: (context) {
|
||||
return BooksScreen(
|
||||
onTap: (idx) {
|
||||
GoRouter.of(context).go(switch (idx) {
|
||||
0 => '/books/popular',
|
||||
1 => '/books/new',
|
||||
2 => '/books/all',
|
||||
_ => '/books/popular',
|
||||
});
|
||||
},
|
||||
selectedIndex: switch (state.uri.path) {
|
||||
var p when p.startsWith('/books/popular') => 0,
|
||||
var p when p.startsWith('/books/new') => 1,
|
||||
var p when p.startsWith('/books/all') => 2,
|
||||
_ => 0,
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/books/popular',
|
||||
pageBuilder: (context, state) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
key: state.pageKey,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return BookList(
|
||||
books: libraryInstance.popularBooks,
|
||||
onTap: (book) {
|
||||
GoRouter.of(context)
|
||||
.go('/books/popular/book/${book.id}');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'book/:bookId',
|
||||
parentNavigatorKey: appShellNavigatorKey,
|
||||
builder: (context, state) {
|
||||
return BookDetailsScreen(
|
||||
book: libraryInstance
|
||||
.getBook(state.pathParameters['bookId'] ?? ''),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/books/new',
|
||||
pageBuilder: (context, state) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
key: state.pageKey,
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return BookList(
|
||||
books: libraryInstance.newBooks,
|
||||
onTap: (book) {
|
||||
GoRouter.of(context)
|
||||
.go('/books/new/book/${book.id}');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'book/:bookId',
|
||||
parentNavigatorKey: appShellNavigatorKey,
|
||||
builder: (context, state) {
|
||||
return BookDetailsScreen(
|
||||
book: libraryInstance
|
||||
.getBook(state.pathParameters['bookId'] ?? ''),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/books/all',
|
||||
pageBuilder: (context, state) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
key: state.pageKey,
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return BookList(
|
||||
books: libraryInstance.allBooks,
|
||||
onTap: (book) {
|
||||
GoRouter.of(context)
|
||||
.go('/books/all/book/${book.id}');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'book/:bookId',
|
||||
parentNavigatorKey: appShellNavigatorKey,
|
||||
builder: (context, state) {
|
||||
return BookDetailsScreen(
|
||||
book: libraryInstance
|
||||
.getBook(state.pathParameters['bookId'] ?? ''),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/authors',
|
||||
pageBuilder: (context, state) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
key: state.pageKey,
|
||||
child: Builder(builder: (context) {
|
||||
return AuthorsScreen(
|
||||
onTap: (author) {
|
||||
GoRouter.of(context)
|
||||
.go('/authors/author/${author.id}');
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'author/:authorId',
|
||||
builder: (context, state) {
|
||||
final author = libraryInstance.allAuthors.firstWhere(
|
||||
(author) =>
|
||||
author.id ==
|
||||
int.parse(state.pathParameters['authorId']!));
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
return Builder(builder: (context) {
|
||||
return AuthorDetailsScreen(
|
||||
author: author,
|
||||
onBookTapped: (book) {
|
||||
GoRouter.of(context)
|
||||
.go('/books/all/book/${book.id}');
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/settings',
|
||||
pageBuilder: (context, state) {
|
||||
return FadeTransitionPage<dynamic>(
|
||||
key: state.pageKey,
|
||||
child: const SettingsScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Future<ParsedRoute> _guard(ParsedRoute from) async {
|
||||
final signedIn = _auth.signedIn;
|
||||
final signInRoute = ParsedRoute('/signin', '/signin', {}, {});
|
||||
|
||||
// Go to /signin if the user is not signed in
|
||||
if (!signedIn && from != signInRoute) {
|
||||
return signInRoute;
|
||||
}
|
||||
// Go to /books if the user is signed in and tries to go to /signin.
|
||||
else if (signedIn && from == signInRoute) {
|
||||
return ParsedRoute('/books/popular', '/books/popular', {}, {});
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
void _handleAuthStateChanged() {
|
||||
if (!_auth.signedIn) {
|
||||
_routeState.go('/signin');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_auth.removeListener(_handleAuthStateChanged);
|
||||
_routeState.dispose();
|
||||
_routerDelegate.dispose();
|
||||
super.dispose();
|
||||
GoRoute(
|
||||
path: '/sign-in',
|
||||
builder: (context, state) {
|
||||
// Use a builder to get the correct BuildContext
|
||||
// TODO (johnpryan): remove when https://github.com/flutter/flutter/issues/108177 lands
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return SignInScreen(
|
||||
onSignIn: (value) async {
|
||||
final router = GoRouter.of(context);
|
||||
await BookstoreAuth.of(context)
|
||||
.signIn(value.username, value.password);
|
||||
router.go('/books/popular');
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user