Cleanup to navigation_and_routing sample (#881)

- make more things private and final, where possible
- remove unused members
- used expression bodies, where possible
pull/886/head
Kevin Moore 4 years ago committed by GitHub
parent 410e43fbc1
commit fa7dec1475
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,12 +18,11 @@ class Bookstore extends StatefulWidget {
} }
class _BookstoreState extends State<Bookstore> { class _BookstoreState extends State<Bookstore> {
final auth = BookstoreAuth(); final _auth = BookstoreAuth();
late final BookstoreRouteGuard guard; final _navigatorKey = GlobalKey<NavigatorState>();
late final RouteState routeState; late final RouteState _routeState;
late final SimpleRouterDelegate routerDelegate; late final SimpleRouterDelegate _routerDelegate;
late final TemplateRouteParser routeParser; late final TemplateRouteParser _routeParser;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final library = Library() final library = Library()
..addBook( ..addBook(
@ -49,10 +48,10 @@ class _BookstoreState extends State<Bookstore> {
@override @override
void initState() { void initState() {
guard = BookstoreRouteGuard(auth: auth); final guard = BookstoreRouteGuard(auth: _auth);
/// Configure the parser with all of the app's allowed path templates. /// Configure the parser with all of the app's allowed path templates.
routeParser = TemplateRouteParser( _routeParser = TemplateRouteParser(
allowedPaths: [ allowedPaths: [
'/signin', '/signin',
'/authors', '/authors',
@ -67,50 +66,48 @@ class _BookstoreState extends State<Bookstore> {
initialRoute: '/signin', initialRoute: '/signin',
); );
routeState = RouteState(routeParser); _routeState = RouteState(_routeParser);
routerDelegate = SimpleRouterDelegate( _routerDelegate = SimpleRouterDelegate(
routeState: routeState, routeState: _routeState,
navigatorKey: navigatorKey, navigatorKey: _navigatorKey,
builder: (context) => BookstoreNavigator( builder: (context) => BookstoreNavigator(
navigatorKey: navigatorKey, navigatorKey: _navigatorKey,
), ),
); );
// Listen for when the user logs out and display the signin screen. // Listen for when the user logs out and display the signin screen.
auth.addListener(_handleAuthStateChanged); _auth.addListener(_handleAuthStateChanged);
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => RouteStateScope(
return RouteStateScope( notifier: _routeState,
notifier: routeState,
child: BookstoreAuthScope( child: BookstoreAuthScope(
notifier: auth, notifier: _auth,
child: LibraryScope( child: LibraryScope(
library: library, library: library,
child: MaterialApp.router( child: MaterialApp.router(
routerDelegate: routerDelegate, routerDelegate: _routerDelegate,
routeInformationParser: routeParser, routeInformationParser: _routeParser,
), ),
), ),
), ),
); );
}
void _handleAuthStateChanged() { void _handleAuthStateChanged() {
if (!auth.signedIn) { if (!_auth.signedIn) {
routeState.go('/signin'); _routeState.go('/signin');
} }
} }
@override @override
void dispose() { void dispose() {
auth.removeListener(_handleAuthStateChanged); _auth.removeListener(_handleAuthStateChanged);
routeState.dispose(); _routeState.dispose();
routerDelegate.dispose(); _routerDelegate.dispose();
super.dispose(); super.dispose();
} }
} }

@ -9,9 +9,7 @@ import 'package:flutter/widgets.dart';
class BookstoreAuth extends ChangeNotifier { class BookstoreAuth extends ChangeNotifier {
bool _signedIn = false; bool _signedIn = false;
bool get signedIn { bool get signedIn => _signedIn;
return _signedIn;
}
Future<void> signOut() async { Future<void> signOut() async {
await Future<void>.delayed(const Duration(milliseconds: 200)); await Future<void>.delayed(const Duration(milliseconds: 200));
@ -30,9 +28,8 @@ class BookstoreAuth extends ChangeNotifier {
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) =>
return other is BookstoreAuth && other._signedIn == _signedIn; other is BookstoreAuth && other._signedIn == _signedIn;
}
@override @override
int get hashCode => _signedIn.hashCode; int get hashCode => _signedIn.hashCode;
@ -45,9 +42,7 @@ class BookstoreAuthScope extends InheritedNotifier<BookstoreAuth> {
Key? key, Key? key,
}) : super(key: key, notifier: notifier, child: child); }) : super(key: key, notifier: notifier, child: child);
static BookstoreAuth? of(BuildContext context) { static BookstoreAuth? of(BuildContext context) => context
return context
.dependOnInheritedWidgetOfExactType<BookstoreAuthScope>() .dependOnInheritedWidgetOfExactType<BookstoreAuthScope>()
?.notifier; ?.notifier;
} }
}

@ -7,7 +7,7 @@ import 'auth.dart';
/// An implementation of [RouteGuard] that redirects to /signIn /// An implementation of [RouteGuard] that redirects to /signIn
class BookstoreRouteGuard implements RouteGuard<ParsedRoute> { class BookstoreRouteGuard implements RouteGuard<ParsedRoute> {
BookstoreAuth auth; final BookstoreAuth auth;
BookstoreRouteGuard({ BookstoreRouteGuard({
required this.auth, required this.auth,

@ -32,15 +32,11 @@ class Library {
allBooks.add(book); allBooks.add(book);
} }
List<Book> get popularBooks { List<Book> get popularBooks => [
return [
...allBooks.where((book) => book.isPopular), ...allBooks.where((book) => book.isPopular),
]; ];
}
List<Book> get newBooks { List<Book> get newBooks => [
return [
...allBooks.where((book) => book.isNew), ...allBooks.where((book) => book.isNew),
]; ];
} }
}

@ -28,9 +28,7 @@ class SimpleRouterDelegate extends RouterDelegate<ParsedRoute>
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => builder(context);
return builder(context);
}
@override @override
Future<void> setNewRoutePath(ParsedRoute configuration) async { Future<void> setNewRoutePath(ParsedRoute configuration) async {
@ -39,9 +37,7 @@ class SimpleRouterDelegate extends RouterDelegate<ParsedRoute>
} }
@override @override
ParsedRoute get currentConfiguration { ParsedRoute get currentConfiguration => routeState.route;
return routeState.route;
}
@override @override
void dispose() { void dispose() {

@ -27,13 +27,12 @@ class ParsedRoute {
this.path, this.pathTemplate, this.parameters, this.queryParameters); this.path, this.pathTemplate, this.parameters, this.queryParameters);
@override @override
bool operator ==(Object other) { bool operator ==(Object other) =>
return other is ParsedRoute && other is ParsedRoute &&
other.pathTemplate == pathTemplate && other.pathTemplate == pathTemplate &&
other.path == path && other.path == path &&
_mapEquality.equals(parameters, other.parameters) && _mapEquality.equals(parameters, other.parameters) &&
_mapEquality.equals(queryParameters, other.queryParameters); _mapEquality.equals(queryParameters, other.queryParameters);
}
@override @override
int get hashCode => hash4( int get hashCode => hash4(

@ -44,9 +44,8 @@ class TemplateRouteParser extends RouteInformationParser<ParsedRoute> {
@override @override
Future<ParsedRoute> parseRouteInformation( Future<ParsedRoute> parseRouteInformation(
RouteInformation routeInformation) async { RouteInformation routeInformation) async =>
return await _parse(routeInformation); await _parse(routeInformation);
}
Future<ParsedRoute> _parse(RouteInformation routeInformation) async { Future<ParsedRoute> _parse(RouteInformation routeInformation) async {
final path = routeInformation.location!; final path = routeInformation.location!;
@ -74,7 +73,6 @@ class TemplateRouteParser extends RouteInformationParser<ParsedRoute> {
} }
@override @override
RouteInformation restoreRouteInformation(ParsedRoute configuration) { RouteInformation restoreRouteInformation(ParsedRoute configuration) =>
return RouteInformation(location: configuration.path); RouteInformation(location: configuration.path);
}
} }

@ -44,9 +44,6 @@ class RouteStateScope extends InheritedNotifier<RouteState> {
Key? key, Key? key,
}) : super(key: key, notifier: notifier, child: child); }) : super(key: key, notifier: notifier, child: child);
static RouteState? of(BuildContext context) { static RouteState? of(BuildContext context) =>
return context context.dependOnInheritedWidgetOfExactType<RouteStateScope>()?.notifier;
.dependOnInheritedWidgetOfExactType<RouteStateScope>()
?.notifier;
}
} }

@ -17,8 +17,7 @@ class AuthorDetailsScreen extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(author.name), title: Text(author.name),
), ),
@ -38,4 +37,3 @@ class AuthorDetailsScreen extends StatelessWidget {
), ),
); );
} }
}

@ -14,8 +14,7 @@ class AuthorsScreen extends StatelessWidget {
const AuthorsScreen({Key? key}) : super(key: key); const AuthorsScreen({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(title), title: Text(title),
), ),
@ -27,4 +26,3 @@ class AuthorsScreen extends StatelessWidget {
), ),
); );
} }
}

@ -45,21 +45,18 @@ class BookDetailsScreen extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.of(context).push<void>( Navigator.of(context).push<void>(
MaterialPageRoute<void>( MaterialPageRoute<void>(
builder: (context) { builder: (context) =>
return AuthorDetailsScreen(author: book!.author); AuthorDetailsScreen(author: book!.author),
},
), ),
); );
}, },
), ),
Link( Link(
uri: Uri.parse('/author/${book!.author.id}'), uri: Uri.parse('/author/${book!.author.id}'),
builder: (context, followLink) { builder: (context, followLink) => TextButton(
return TextButton(
onPressed: followLink, onPressed: followLink,
child: const Text('View author (Link)'), child: const Text('View author (Link)'),
); ),
},
), ),
], ],
), ),

@ -10,11 +10,8 @@ import '../widgets/book_list.dart';
import '../widgets/library_scope.dart'; import '../widgets/library_scope.dart';
class BooksScreen extends StatefulWidget { class BooksScreen extends StatefulWidget {
final ParsedRoute currentRoute;
const BooksScreen({ const BooksScreen({
Key? key, Key? key,
required this.currentRoute,
}) : super(key: key); }) : super(key: key);
@override @override
@ -36,7 +33,7 @@ class _BooksScreenState extends State<BooksScreen>
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
final newPath = routeState.route.pathTemplate; final newPath = _routeState.route.pathTemplate;
if (newPath.startsWith('/books/popular')) { if (newPath.startsWith('/books/popular')) {
_tabController.index = 0; _tabController.index = 0;
} else if (newPath.startsWith('/books/new')) { } else if (newPath.startsWith('/books/new')) {
@ -96,35 +93,23 @@ class _BooksScreenState extends State<BooksScreen>
); );
} }
String get title { RouteState get _routeState => RouteStateScope.of(context)!;
switch (_tabController.index) {
case 1:
return 'New';
case 2:
return 'All';
case 0:
default:
return 'Popular';
}
}
RouteState get routeState => RouteStateScope.of(context)!;
void _handleBookTapped(Book book) { void _handleBookTapped(Book book) {
routeState.go('/book/${book.id}'); _routeState.go('/book/${book.id}');
} }
void _handleTabIndexChanged() { void _handleTabIndexChanged() {
switch (_tabController.index) { switch (_tabController.index) {
case 1: case 1:
routeState.go('/books/new'); _routeState.go('/books/new');
break; break;
case 2: case 2:
routeState.go('/books/all'); _routeState.go('/books/all');
break; break;
case 0: case 0:
default: default:
routeState.go('/books/popular'); _routeState.go('/books/popular');
break; break;
} }
} }

@ -30,10 +30,10 @@ class BookstoreNavigator extends StatefulWidget {
} }
class _BookstoreNavigatorState extends State<BookstoreNavigator> { class _BookstoreNavigatorState extends State<BookstoreNavigator> {
final signInKey = const ValueKey('Sign in'); final _signInKey = const ValueKey('Sign in');
final scaffoldKey = const ValueKey<String>('App scaffold'); final _scaffoldKey = const ValueKey<String>('App scaffold');
final bookDetailsKey = const ValueKey<String>('Book details screen'); final _bookDetailsKey = const ValueKey<String>('Book details screen');
final authorDetailsKey = const ValueKey<String>('Author details screen'); final _authorDetailsKey = const ValueKey<String>('Author details screen');
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -60,12 +60,12 @@ class _BookstoreNavigatorState extends State<BookstoreNavigator> {
// When a page that is stacked on top of the scaffold is popped, display // When a page that is stacked on top of the scaffold is popped, display
// the /books or /authors tab in BookstoreScaffold. // the /books or /authors tab in BookstoreScaffold.
if (route.settings is Page && if (route.settings is Page &&
(route.settings as Page).key == bookDetailsKey) { (route.settings as Page).key == _bookDetailsKey) {
routeState.go('/books/popular'); routeState.go('/books/popular');
} }
if (route.settings is Page && if (route.settings is Page &&
(route.settings as Page).key == authorDetailsKey) { (route.settings as Page).key == _authorDetailsKey) {
routeState.go('/authors'); routeState.go('/authors');
} }
@ -75,7 +75,7 @@ class _BookstoreNavigatorState extends State<BookstoreNavigator> {
if (routeState.route.pathTemplate == '/signin') if (routeState.route.pathTemplate == '/signin')
// Display the sign in screen. // Display the sign in screen.
FadeTransitionPage<void>( FadeTransitionPage<void>(
key: signInKey, key: _signInKey,
child: SignInScreen( child: SignInScreen(
onSignIn: (credentials) async { onSignIn: (credentials) async {
var signedIn = await authState.signIn( var signedIn = await authState.signIn(
@ -89,21 +89,21 @@ class _BookstoreNavigatorState extends State<BookstoreNavigator> {
else ...[ else ...[
// Display the app // Display the app
FadeTransitionPage<void>( FadeTransitionPage<void>(
key: scaffoldKey, key: _scaffoldKey,
child: const BookstoreScaffold(), child: const BookstoreScaffold(),
), ),
// Add an additional page to the stack if the user is viewing a book // Add an additional page to the stack if the user is viewing a book
// or an author // or an author
if (selectedBook != null) if (selectedBook != null)
MaterialPage<void>( MaterialPage<void>(
key: bookDetailsKey, key: _bookDetailsKey,
child: BookDetailsScreen( child: BookDetailsScreen(
book: selectedBook, book: selectedBook,
), ),
) )
else if (selectedAuthor != null) else if (selectedAuthor != null)
MaterialPage<void>( MaterialPage<void>(
key: authorDetailsKey, key: _authorDetailsKey,
child: AuthorDetailsScreen( child: AuthorDetailsScreen(
author: selectedAuthor, author: selectedAuthor,
), ),

@ -41,9 +41,9 @@ class BookstoreScaffoldBody extends StatelessWidget {
) )
else if (currentRoute.pathTemplate.startsWith('/books') || else if (currentRoute.pathTemplate.startsWith('/books') ||
currentRoute.pathTemplate == '/') currentRoute.pathTemplate == '/')
FadeTransitionPage<void>( const FadeTransitionPage<void>(
key: const ValueKey('books'), key: ValueKey('books'),
child: BooksScreen(currentRoute: currentRoute), child: BooksScreen(),
) )
// Avoid building a Navigator with an empty `pages` list when the // Avoid building a Navigator with an empty `pages` list when the

@ -17,8 +17,7 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
body: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Align( child: Align(
@ -37,7 +36,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
); );
} }
}
class SettingsContent extends StatelessWidget { class SettingsContent extends StatelessWidget {
const SettingsContent({ const SettingsContent({
@ -45,8 +43,7 @@ class SettingsContent extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Column(
return Column(
children: [ children: [
...[ ...[
Text( Text(
@ -61,12 +58,10 @@ class SettingsContent extends StatelessWidget {
), ),
Link( Link(
uri: Uri.parse('/book/0'), uri: Uri.parse('/book/0'),
builder: (context, followLink) { builder: (context, followLink) => TextButton(
return TextButton(
onPressed: followLink, onPressed: followLink,
child: const Text('Go directly to /book/0 (Link)'), child: const Text('Go directly to /book/0 (Link)'),
); ),
},
), ),
TextButton( TextButton(
child: const Text('Go directly to /book/0 (RouteState)'), child: const Text('Go directly to /book/0 (RouteState)'),
@ -98,4 +93,3 @@ class SettingsContent extends StatelessWidget {
], ],
); );
} }
}

@ -29,8 +29,7 @@ class _SignInScreenState extends State<SignInScreen> {
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(
return Scaffold(
body: Center( body: Center(
child: Card( child: Card(
child: Container( child: Container(
@ -68,4 +67,3 @@ class _SignInScreenState extends State<SignInScreen> {
), ),
); );
} }
}

@ -17,11 +17,9 @@ class AuthorList extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => ListView.builder(
return ListView.builder(
itemCount: authors.length, itemCount: authors.length,
itemBuilder: (context, index) { itemBuilder: (context, index) => ListTile(
return ListTile(
title: Text( title: Text(
authors[index].name, authors[index].name,
), ),
@ -29,8 +27,6 @@ class AuthorList extends StatelessWidget {
'${authors[index].books.length} books', '${authors[index].books.length} books',
), ),
onTap: onTap != null ? () => onTap!(authors[index]) : null, onTap: onTap != null ? () => onTap!(authors[index]) : null,
),
); );
},
);
}
} }

@ -17,11 +17,9 @@ class BookList extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => ListView.builder(
return ListView.builder(
itemCount: books.length, itemCount: books.length,
itemBuilder: (context, index) { itemBuilder: (context, index) => ListTile(
return ListTile(
title: Text( title: Text(
books[index].title, books[index].title,
), ),
@ -29,8 +27,6 @@ class BookList extends StatelessWidget {
books[index].author.name, books[index].author.name,
), ),
onTap: onTap != null ? () => onTap!(books[index]) : null, onTap: onTap != null ? () => onTap!(books[index]) : null,
),
); );
},
);
}
} }

@ -15,15 +15,14 @@ class FadeTransitionPage<T> extends Page<T> {
}) : super(key: key); }) : super(key: key);
@override @override
Route<T> createRoute(BuildContext context) { Route<T> createRoute(BuildContext context) =>
return PageBasedFadeTransitionRoute<T>(this); PageBasedFadeTransitionRoute<T>(this);
}
} }
class PageBasedFadeTransitionRoute<T> extends PageRoute<T> { class PageBasedFadeTransitionRoute<T> extends PageRoute<T> {
final FadeTransitionPage<T> page; final FadeTransitionPage<T> _page;
PageBasedFadeTransitionRoute(this.page) : super(settings: page); PageBasedFadeTransitionRoute(this._page) : super(settings: _page);
@override @override
Color? get barrierColor => null; Color? get barrierColor => null;
@ -32,7 +31,7 @@ class PageBasedFadeTransitionRoute<T> extends PageRoute<T> {
String? get barrierLabel => null; String? get barrierLabel => null;
@override @override
Duration get transitionDuration => page.duration; Duration get transitionDuration => _page.duration;
@override @override
bool get maintainState => true; bool get maintainState => true;
@ -49,7 +48,6 @@ class PageBasedFadeTransitionRoute<T> extends PageRoute<T> {
@override @override
Widget buildTransitions(BuildContext context, Animation<double> animation, Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) { Animation<double> secondaryAnimation, Widget child) =>
return child; child;
}
} }

@ -19,7 +19,6 @@ class LibraryScope extends InheritedWidget {
bool updateShouldNotify(LibraryScope oldWidget) => bool updateShouldNotify(LibraryScope oldWidget) =>
library != oldWidget.library; library != oldWidget.library;
static Library of(BuildContext context) { static Library of(BuildContext context) =>
return context.dependOnInheritedWidgetOfExactType<LibraryScope>()!.library; context.dependOnInheritedWidgetOfExactType<LibraryScope>()!.library;
}
} }

Loading…
Cancel
Save