mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 22:09:06 +00:00
Cleanup to navigation_and_routing sample (#881)
- make more things private and final, where possible - remove unused members - used expression bodies, where possible
This commit is contained in:
@@ -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,25 +17,23 @@ 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),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: BookList(
|
|
||||||
books: author.books,
|
|
||||||
onTap: (book) {
|
|
||||||
RouteStateScope.of(context)!.go('/book/${book.id}');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
body: Center(
|
||||||
);
|
child: Column(
|
||||||
}
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: BookList(
|
||||||
|
books: author.books,
|
||||||
|
onTap: (book) {
|
||||||
|
RouteStateScope.of(context)!.go('/book/${book.id}');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,15 @@ 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),
|
),
|
||||||
),
|
body: AuthorList(
|
||||||
body: AuthorList(
|
authors: LibraryScope.of(context).allAuthors,
|
||||||
authors: LibraryScope.of(context).allAuthors,
|
onTap: (author) {
|
||||||
onTap: (author) {
|
RouteStateScope.of(context)!.go('/author/${author.id}');
|
||||||
RouteStateScope.of(context)!.go('/author/${author.id}');
|
},
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,26 +17,24 @@ 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(
|
alignment: Alignment.topCenter,
|
||||||
alignment: Alignment.topCenter,
|
child: ConstrainedBox(
|
||||||
child: ConstrainedBox(
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
constraints: const BoxConstraints(maxWidth: 400),
|
child: const Card(
|
||||||
child: const Card(
|
child: Padding(
|
||||||
child: Padding(
|
padding: EdgeInsets.symmetric(vertical: 18, horizontal: 12),
|
||||||
padding: EdgeInsets.symmetric(vertical: 18, horizontal: 12),
|
child: SettingsContent(),
|
||||||
child: SettingsContent(),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsContent extends StatelessWidget {
|
class SettingsContent extends StatelessWidget {
|
||||||
@@ -45,57 +43,53 @@ 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(
|
'Settings',
|
||||||
'Settings',
|
style: Theme.of(context).textTheme.headline4,
|
||||||
style: Theme.of(context).textTheme.headline4,
|
),
|
||||||
),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
onPressed: () {
|
||||||
onPressed: () {
|
BookstoreAuthScope.of(context)!.signOut();
|
||||||
BookstoreAuthScope.of(context)!.signOut();
|
},
|
||||||
},
|
child: const Text('Sign out'),
|
||||||
child: const Text('Sign out'),
|
),
|
||||||
),
|
Link(
|
||||||
Link(
|
uri: Uri.parse('/book/0'),
|
||||||
uri: Uri.parse('/book/0'),
|
builder: (context, followLink) => TextButton(
|
||||||
builder: (context, followLink) {
|
|
||||||
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(
|
|
||||||
child: const Text('Go directly to /book/0 (RouteState)'),
|
|
||||||
onPressed: () {
|
|
||||||
RouteStateScope.of(context)!.go('/book/0');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
].map((w) => Padding(padding: const EdgeInsets.all(8), child: w)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => showDialog<String>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: const Text('Alert!'),
|
|
||||||
content: const Text('The alert description goes here.'),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
|
||||||
child: const Text('Cancel'),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, 'OK'),
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
TextButton(
|
||||||
child: const Text('Show Dialog'),
|
child: const Text('Go directly to /book/0 (RouteState)'),
|
||||||
)
|
onPressed: () {
|
||||||
],
|
RouteStateScope.of(context)!.go('/book/0');
|
||||||
);
|
},
|
||||||
}
|
),
|
||||||
|
].map((w) => Padding(padding: const EdgeInsets.all(8), child: w)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Alert!'),
|
||||||
|
content: const Text('The alert description goes here.'),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, 'OK'),
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text('Show Dialog'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,43 +29,41 @@ 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(
|
constraints: BoxConstraints.loose(const Size(600, 600)),
|
||||||
constraints: BoxConstraints.loose(const Size(600, 600)),
|
padding: const EdgeInsets.all(8),
|
||||||
padding: const EdgeInsets.all(8),
|
child: Column(
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Text('Sign in', style: Theme.of(context).textTheme.headline4),
|
||||||
Text('Sign in', style: Theme.of(context).textTheme.headline4),
|
TextField(
|
||||||
TextField(
|
decoration: const InputDecoration(labelText: 'Username'),
|
||||||
decoration: const InputDecoration(labelText: 'Username'),
|
controller: _usernameController,
|
||||||
controller: _usernameController,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(labelText: 'Password'),
|
|
||||||
obscureText: true,
|
|
||||||
controller: _passwordController,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
widget.onSignIn(Credentials(
|
|
||||||
_usernameController.value.text,
|
|
||||||
_passwordController.value.text));
|
|
||||||
},
|
|
||||||
child: const Text('Sign in'),
|
|
||||||
),
|
),
|
||||||
),
|
TextField(
|
||||||
],
|
decoration: const InputDecoration(labelText: 'Password'),
|
||||||
|
obscureText: true,
|
||||||
|
controller: _passwordController,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
widget.onSignIn(Credentials(
|
||||||
|
_usernameController.value.text,
|
||||||
|
_passwordController.value.text));
|
||||||
|
},
|
||||||
|
child: const Text('Sign in'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => ListTile(
|
||||||
itemBuilder: (context, index) {
|
|
||||||
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) => ListTile(
|
||||||
itemBuilder: (context, index) {
|
|
||||||
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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user