Created
July 7, 2021 21:10
-
-
Save chunhtai/92d4e6b76d241527ed3c8ee1ffba034f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(BooksApp()); | |
} | |
class Book { | |
final String title; | |
final String author; | |
Book(this.title, this.author); | |
} | |
class BooksApp extends StatefulWidget { | |
@override | |
State<StatefulWidget> createState() => _BooksAppState(); | |
} | |
class _BooksAppState extends State<BooksApp> { | |
final BookRouterDelegate _routerDelegate = BookRouterDelegate(); | |
final BookRouteInformationParser _routeInformationParser = | |
BookRouteInformationParser(); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp.router( | |
title: 'Books App', | |
routerDelegate: _routerDelegate, | |
routeInformationParser: _routeInformationParser, | |
); | |
} | |
} | |
class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> { | |
@override | |
Future<BookRoutePath> parseRouteInformation( | |
RouteInformation routeInformation) async { | |
final uri = Uri.parse(routeInformation.location); | |
if (uri.pathSegments.length >= 2) { | |
var remaining = uri.pathSegments[1]; | |
return BookRoutePath.details(int.tryParse(remaining)); | |
} else { | |
return BookRoutePath.home(); | |
} | |
} | |
@override | |
RouteInformation restoreRouteInformation(BookRoutePath path) { | |
if (path.isHomePage) { | |
return RouteInformation(location: '/'); | |
} | |
if (path.isDetailsPage) { | |
return RouteInformation(location: '/book/${path.id}'); | |
} | |
return null; | |
} | |
} | |
class BookRouterDelegate extends RouterDelegate<BookRoutePath> | |
with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> { | |
@override | |
final GlobalKey<NavigatorState> navigatorKey; | |
Book _selectedBook; | |
Book fahrenheit = Book('Fahrenheit 451', 'Ray Bradbury'); | |
List<Book> books = [ | |
Book('Stranger in a Strange Land', 'Robert A. Heinlein'), | |
Book('Foundation', 'Isaac Asimov'), | |
Book('Fahrenheit 451', 'Ray Bradbury'), | |
]; | |
BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>(); | |
@override | |
BookRoutePath get currentConfiguration => _selectedBook == null | |
? BookRoutePath.home() | |
: BookRoutePath.details(books.indexOf(_selectedBook)); | |
@override | |
Widget build(BuildContext context) { | |
return Navigator( | |
key: navigatorKey, | |
transitionDelegate: NoAnimationTransitionDelegate(), | |
pages: [ | |
if (_selectedBook == null) | |
BookDetailsPage2(book: books[0], onTapped: _handleBookTapped), | |
if (_selectedBook != null) ...[ | |
BookDetailsPage( | |
book: Book('Fahrenheit 451', 'Ray Bradbury'), | |
), | |
BookDetailsPage2(book: _selectedBook, onTapped: _handleBookTapped), | |
] | |
], | |
onPopPage: (route, result) { | |
if (!route.didPop(result)) { | |
return false; | |
} | |
// Update the list of pages by setting _selectedBook to null | |
// _selectedBook = n; | |
notifyListeners(); | |
return true; | |
}, | |
); | |
} | |
@override | |
Future<void> setNewRoutePath(BookRoutePath path) async { | |
if (path.isDetailsPage) { | |
_selectedBook = books[path.id]; | |
} | |
} | |
void _handleBookTapped() { | |
_selectedBook = books[0]; | |
notifyListeners(); | |
} | |
} | |
class BookDetailsPage extends Page { | |
final Book book; | |
BookDetailsPage({ | |
this.book, | |
}) : super(key: ValueKey(2)); | |
@override | |
Route createRoute(BuildContext context) { | |
return MaterialPageRoute( | |
settings: this, | |
builder: (BuildContext context) { | |
return BookDetailsScreen(book: book); | |
}, | |
); | |
} | |
} | |
class BookDetailsPage2 extends Page { | |
final Book book; | |
final VoidCallback onTapped; | |
BookDetailsPage2({ | |
this.book, | |
this.onTapped, | |
}) : super(key: ValueKey(1)); | |
@override | |
Route createRoute(BuildContext context) { | |
return MaterialPageRoute( | |
settings: this, | |
builder: (BuildContext context) { | |
return BookDetailsScreen2(book: book, onTapped: onTapped); | |
}, | |
); | |
} | |
} | |
class BookRoutePath { | |
final int id; | |
BookRoutePath.home() : id = null; | |
BookRoutePath.details(this.id); | |
bool get isHomePage => id == null; | |
bool get isDetailsPage => id != null; | |
} | |
class BooksListScreen extends StatelessWidget { | |
final List<Book> books; | |
final ValueChanged<Book> onTapped; | |
BooksListScreen({ | |
@required this.books, | |
@required this.onTapped, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Focus( | |
debugLabel: 'BooksListScreen', | |
child: Scaffold( | |
appBar: AppBar(), | |
body: ListView( | |
children: [ | |
for (var book in books) | |
ListTile( | |
title: Text(book.title), | |
subtitle: Text(book.author), | |
onTap: () => onTapped(book), | |
) | |
], | |
), | |
), | |
); | |
} | |
} | |
class BookDetailsScreen2 extends StatelessWidget { | |
final Book book; | |
final VoidCallback onTapped; | |
BookDetailsScreen2({ | |
@required this.book, | |
@required this.onTapped, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(), | |
body: Actions( | |
actions: { | |
ActivateIntent: CallbackAction( | |
onInvoke: (_) => print(book.title), | |
), | |
}, | |
child: Focus( | |
debugLabel: 'BookDetailsScreen2', | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
TextField(onTap: onTapped,), | |
if (book != null) ...[ | |
Text(book.title, | |
style: Theme.of(context).textTheme.headline6), | |
Text(book.author, | |
style: Theme.of(context).textTheme.subtitle1), | |
], | |
], | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
class BookDetailsScreen extends StatelessWidget { | |
final Book book; | |
final FocusNode node = FocusNode(); | |
BookDetailsScreen({ | |
@required this.book, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
node.requestFocus(); | |
return Scaffold( | |
appBar: AppBar(), | |
body: Actions( | |
actions: { | |
ActivateIntent: CallbackAction( | |
onInvoke: (_) => print(book.title), | |
), | |
}, | |
child: Focus( | |
// focusNode: node, | |
autofocus: ModalRoute.of(context).isCurrent, | |
debugLabel: book.title, | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
TextField(), | |
if (book != null) ...[ | |
Text(book.title, | |
style: Theme.of(context).textTheme.headline6), | |
Text(book.author, | |
style: Theme.of(context).textTheme.subtitle1), | |
], | |
], | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
class NoAnimationTransitionDelegate extends TransitionDelegate<void> { | |
@override | |
Iterable<RouteTransitionRecord> resolve({ | |
List<RouteTransitionRecord> newPageRouteHistory, | |
Map<RouteTransitionRecord, RouteTransitionRecord> | |
locationToExitingPageRoute, | |
Map<RouteTransitionRecord, List<RouteTransitionRecord>> | |
pageRouteToPagelessRoutes, | |
}) { | |
final results = <RouteTransitionRecord>[]; | |
for (final pageRoute in newPageRouteHistory) { | |
if (pageRoute.isWaitingForEnteringDecision) { | |
pageRoute.markForAdd(); | |
} | |
results.add(pageRoute); | |
} | |
for (final exitingPageRoute in locationToExitingPageRoute.values) { | |
if (exitingPageRoute.isWaitingForExitingDecision) { | |
exitingPageRoute.markForRemove(); | |
final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute]; | |
if (pagelessRoutes != null) { | |
for (final pagelessRoute in pagelessRoutes) { | |
pagelessRoute.markForRemove(); | |
} | |
} | |
} | |
results.add(exitingPageRoute); | |
} | |
return results; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment