back_button_interceptor 8.0.0 back_button_interceptor: ^8.0.0 copied to clipboard
Back Button Interceptor. May be used to do stuff when the Android back-button is tapped, as an alternative to `WillPopScope`.
back_button_interceptor #
In simple cases, when you need to intercept the Android back-button, you usually add WillPopScope
to your widget tree. However, when developing stateful widgets that interact with the back button,
it's more convenient to use the BackButtonInterceptor
.
You may add interceptor functions to be called when the back button is tapped. These functions
may perform some useful work, and then, if any of them return true
, the default button process
(usually popping a Route) will not be fired.
Android 13 #
To make it work on Android 13 and up, set this to false
in the Android manifest:
android:enableOnBackInvokedCallback="false"
In more detail #
All added functions are called, in order. If any function returns true
, the
combined result is true
, and the default button process will NOT be fired. Only if all functions
return false
(or null
), the combined result is false
, and the default button process will be
fired.
Optionally, you may provide a z-index. Functions with a valid z-index will be called before functions with a null z-index, and functions with larger z-index will be called first. When they have the same z-index, functions added last are called first.
Each function gets the boolean stopDefaultButtonEvent
that indicates the current combined result
from all the previous functions. So, if some function doesn't want to fire if some other previous
function already fired, it can do:
if (stopDefaultButtonEvent) return false;
The same result may be obtained if the optional ifNotYetIntercepted
parameter is true. Then the
function will only be called if all previous functions returned false
(that is, if stopDefaultButtonEvent
is false).
Notes #
-
After you've finished you MUST remove each function by calling the
remove()
method. Alternatively, you may also provide aname
when adding a function, and then later remove it by calling theremoveByName(name)
method. -
If any of your interceptors throw an error, the error message will be printed to the console, but the error thrown will be a general error with not much information. You can change the treatment of errors by changing the static
errorProcessing
field. -
Your functions can also process information about routes by using the function's
RouteInfo info
parameter. To get the current route in the navigator, callinfo.currentRoute(context)
. Also,info.routeWhenAdded
contains the route that was the current one when the interceptor was added through theBackButtonInterceptor.add()
method. -
You can set up some function so that it only runs when the current route is the same route of when the interceptor was created. To that end, you can use
info.ifRouteChanged()
method:bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) { if (info.ifRouteChanged(context)) return false; ... }
This means the interceptor function is temporarily disabled while, for example, a dialog is open.
-
You can set up some function so that it only runs in certain routes:
bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) { if (["myRoute1", "myRoute2", "myRoute3"] .contains(info.currentRoute(context))) return false; ... }
-
Both
info.routeWhenAdded
andinfo.ifRouteChanged()
only work if you passed thecontext
parameter to theBackButtonInterceptor.add()
method. Otherwise,info.routeWhenAdded
will benull
andinfo.ifRouteChanged()
will thrown an error. -
The current route can also be obtained by using the static method
BackButtonInterceptor.getCurrentNavigatorRouteName(context)
. -
The interceptor function can return
bool
orFuture<bool>
.
Examples #
Intercepting #
@override
void initState() {
super.initState();
BackButtonInterceptor.add(myInterceptor);
}
@override
void dispose() {
BackButtonInterceptor.remove(myInterceptor);
super.dispose();
}
bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
print("BACK BUTTON!"); // Do some stuff.
return true;
}
Named function and z-index #
@override
void initState() {
super.initState();
BackButtonInterceptor.add(myInterceptor, zIndex:2, name:"SomeName");
}
@override
void dispose() {
BackButtonInterceptor.removeByName("SomeName");
super.dispose();
}
bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
print("BACK BUTTON!"); // Do some stuff.
return true;
}
Runnable Examples #
-
main
Intercepts the back-button and prints a string to the console.
-
main
Intercepts the back-button and prints a string to the console, by using an async interceptor function.
-
main_complex_example
The first screen has a button which opens a second screen. The second screen has 3 red squares. By tapping the Android back-button (or the "pop" button) each square turns blue, one by one. Only when all squares are blue, tapping the back-button once more will return to the previous screen. Also, if you click the "Open Dialog" button, the interceptors are disabled while the dialog is open.
-
main_complex_example_test
This package is test friendly, and this examples shows how to test the back button.
Testing #
For testing purposes, the BackButtonInterceptor.popRoute()
method may be called directly, to
simulate pressing the back button. The list of all fired functions and their results is
recorded in the BackButtonInterceptor.results
static variable.
Note: You may want to add BackButtonInterceptor.removeAll();
to your test's setUp
function, so that you remove old interceptors between tests.
Debugging #
In complex cases, to make it easier for you to debug your interceptors, you may print them to the console, with their names and z-indexes, by doing:
print(BackButtonInterceptor.describe());
See also: #
- https://docs.flutter.io/flutter/widgets/WillPopScope-class.html
- https://stackoverflow.com/questions/45916658/de-activate-system-back-button-in-flutter-app-toddler-navigation
The Flutter packages I've authored:
- async_redux
- fast_immutable_collections
- provider_for_redux
- i18n_extension
- align_positioned
- network_to_file_image
- image_pixels
- matrix4_transform
- back_button_interceptor
- indexed_list_view
- animated_size_and_fade
- assorted_layout_widgets
- weak_map
- themed
- bdd_framework
My Medium Articles:
- Async Redux: Flutter’s non-boilerplate version of Redux ( versions: Português)
- i18n_extension ( versions: Português)
- Flutter: The Advanced Layout Rule Even Beginners Must Know ( versions: русский)
- The New Way to create Themes in your Flutter App
My article in the official Flutter documentation:
Marcelo Glasberg:
https://github.com/marcglasberg
https://linkedin.com/in/marcglasberg/
https://twitter.com/glasbergmarcelo
https://stackoverflow.com/users/3411681/marcg
https://medium.com/@marcglasberg