1
0
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:
John Ryan
2023-11-01 12:14:00 -07:00
committed by GitHub
parent 3e4e3bc784
commit 49eebf8c20
18 changed files with 332 additions and 733 deletions

View File

@@ -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');
},
);
},
);
},
),
],
),
);
}
}