Receiving result from modal routes in Flutter with app_state package
This is a part of the series on app_state
package. The beginning is here.
In the previous part, we made an app with two tabs with independent navigation stacks in each tab. But we only had been passing data to newly opened pages. Let’s learn to do the reverse.
In pure Flutter, we open new screens with Navigator’s push
method. It returns a Future which completes when that screen is closed. The screen does its job and then calls pop()
to close. Whatever is passed to pop
is returned to the code that had called push
in the first place.
This still works with app_state
package, because Navigator
may contain not only app_state’s pages, but any routes. But then in Screen2
you don’t get URLs, browser navigation, and many useful things.
With app_state
, we do not use Future
for screen results. This is because in Dart futures do not survive page reloads. If we need to re-construct the page stack from a URL alone, the bottom page would not get to the state where it awaits something from the top page.
So we use events instead of futures. The example project can be found here.
On the “About” tab we show who this software is licensed to. On button click, we show a screen to enter the name. When the name is saved, it is passed back to the first screen and shown.
What is unusual is that we can start the app at the URL of the second screen, enter the name, and it will be passed to the “About” screen.
Here is lib/pages
directory with new files highlighted:
How screens get closed
Each PageBloc
has events
stream. PageStackBloc
listens to its pages’ event streams, and this allows page blocs to coöperate.
The bloc can call pop(data)
method with an optional data argument. It emits the PageBlocCloseEvent
that carries this data. Under the hood, Android back button calls this method without data. Also under the hood, the back arrow button in AppBar
implicitly triggers the same event if used under PageStackBlocNavigator
.
PageStackBloc
receives this event and calls didPopNext
method on the PageBloc
immediately under. The event is passed there. This way, the bloc learns that it is likely the topmost one now.
Also PageStackBloc
completes the future that was created with the original push
.
So the bottom bloc has two ways to receive the data.
Let’s look at the code.
The input screen
The input screen must have a bloc for many reasons: the screen has a state (the TextEditingController
), and the bloc must emit the close event with data.
We use PageStatefulBloc
for this. It is PageBloc
that also has states
stream in addition to events
. It makes a classic bloc:
It has TextEditingController
to store the input state. In the constructor, we add a listener to this controller so that when the text changes, the bloc emits a new state. For this, we use emitState()
method of PageStatefulBloc
. It emits whatever createState()
method returns.
The state is simple. It only has a bool
to enable or disable the Save button.
The bloc also has onSavePressed()
method which the screen will call when the button is pressed. It calls closeScreenWith
and passes the event.
The bloc also has initialState
field for convenience. It allows the screen to assume there is always some state and not handle the case where states
stream has null
for data.
The screen itself is simple:
It gets rebuilt when the bloc emits a new state.
It has ‘Cancel’ button that is always active and calls bloc.pop()
, the built method that sends PageBlocCloseEvent
with no data.
It has Save button that is only active if the user’s name is not empty. It calls the bloc’s onSavePressed
that sends a close event with data.
Also note that InputPath
has overridden defaultStackPaths
getter, just like we did with book details page. It allows it to have the ‘About’ screen under it when the URL is typed in.
The opener screen
The About screen has also got its bloc for many reasons: it has a state now, it has to open the input screen, and it has to listen to the input screen’s result. The only notable thing is didPopNext
method:
And the screen is trivial.
Run the app and check the result. Copy the URL of the input screen and paste it in a new browser tab to see how it re-creates the page stack. Input a name and see it passed to the “About” screen.
This is it for now. Please like this package if you find it useful.
If you have navigation cases not covered in this series, please leave a comment, and I will look into it.
If you have any issues with app_state package, please leave a comment here or submit an issue in GitHub.