Reputation: 1164
I am following this Navigator 2.0 article : https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade and I am able to run it and trying to understand various parts of it by making simple modifications.
I am having trouble understanding the role of the "pages" parameter that is being passed while building the navigator. My understanding is that pages is a list of pages that represent the stack of screens and the top most screen on that stack is what is displayed to the user. Further, when the user presses the back button, the top most screen is removed (popped) and the one below it becomes active. Is this correct understanding?
To test my understanding, I modified the build method of BookRouterDelegate as follows (I just added a RandomScreen1 to the pages list at the beginning):
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
//------------ ADDED THE FOLLOWING -----
MaterialPage(
key: ValueKey('RandomScreen1Page'),
child: RandomScreen1(
),
),
//----- END ADDITION----------
MaterialPage(
key: ValueKey('BooksListPage'),
child: BooksListScreen(
books: books,
onTapped: _handleBookTapped,
),
),
if (show404)
MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen())
else if (_selectedBook != null)
BookDetailsPage(book: _selectedBook!)
],
onPopPage: (route, result) {
print("onpoproute ${route} ${result} ");
if (!route.didPop(result)) {
print("route did not pop");
return false;
}
// Update the list of pages by setting _selectedBook to null
_selectedBook = null;
show404 = false;
notifyListeners();
return true;
},
);
}
I was expecting that when the user is on book list screen and presses the back button, the RandomScreen will be displayed. But that is not what is happening. What happens is the RandomScreen is displayed for a fraction of a second, and then the BookList screen is shown.
I don't understand why. Is the pages list supposed to have exactly one page?
Upvotes: 1
Views: 1092
Reputation: 24736
This is an old question, but it came up as a top result when googling about the Pages API so it deserves an answer.
The difference between the Navigator API and the Pages API is that the Pages API is declarative. At a high-level, what this means is that instead of relying on some abstract logic to internally update the stack of your app, you need to explicitly describe what the stack is so the app can update itself accordingly. In your case, pushing the back button may have triggered the app to show the animation, but since the pages
stack list hasn't changed, your app is going to revert back to the state it was in before the button was pressed.
What you need to do in onPopPage
(which has been deprecated by onDidRemovePage
since this question was asked) is remove the page that has been popped from the list you pass to pages
. This means that you will need to maintain your own pages
list that you can pass to the parameter.
Here is the new _BooksAppState
code with this principle in mind:
class _BooksAppState extends State<BooksApp> {
final List<Book> books = [
Book('Left Hand of Darkness', 'Ursula K. Le Guin'),
Book('Too Like the Lightning', 'Ada Palmer'),
Book('Kindred', 'Octavia E. Butler'),
];
// This is the pages field that contains the stack we will be maintaining
late List<Page> pages;
@override
void initState() {
super.initState();
// We need to initialize the pages stack when the widget first loads
pages = [
// Example new top level page
MaterialPage(
key: const ValueKey('RandomPage'),
child: Container(color: Colors.red),
),
// Normal top level page
MaterialPage(
key: const ValueKey('BooksListPage'),
child: BooksListScreen(
books: books,
onTapped: _handleBookTapped,
),
),
];
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Books App',
home: Navigator(
// Load in our stack instead of hard-coding some pages
pages: pages,
// This replaces the deprecated `onPopPage` from the original example
onDidRemovePage: (page) {
// Check to make sure we aren't accidentally popping the top page
if (page is BookDetailsPage ||
page.key == const ValueKey('BooksListPage')) {
setState(() {
pages.removeLast();
});
}
},
),
);
}
void _handleBookTapped(Book book) {
setState(() {
// Instead of indirectly controlling the stack with a nullable field,
// manipulate the stack list directly
pages = [...pages, BookDetailsPage(book: book)];
});
}
}
(Keep in mind that this is an example to illustrate the process, not a code snippet that is indicative of best practices. Additionally, a lot of this is abstracted away if you use another approach like the Router API.)
Upvotes: 0