Flutter exact lifecycle equivalents to onResume / onPause on Android and viewWillAppear / viewDidDisappear on iOS
I've found a few solutions, each with its own pros and cons. The one that answers this question the best is FocusDetector.
Best Pick
FocusDetector (Edson Bueno)
FocusDetector handles all the cases covered in the original question. Instead of overrides (like initState() and dispose()), you supply callback functions to a wrapping widget called FocusDetector
. The two relevant callbacks are:
-
onFocusGained
=onResume
-
onFocusLost
=onPause
-
Cons
- Isn’t maintained by Flutter or Google (but it uses
VisibilityDetector
andWigetBindingObserver
under the hood) - The default
VisibilityDetectorController.updateInterval
is 500ms which means the events get fired somewhat late.
- Isn’t maintained by Flutter or Google (but it uses
Borrowing the style from one of my favorite posts:
Example Widget
class PageState extends State<Page> {
@override
void initState() {
super.initState();
log("onCreate / viewDidLoad / initState");
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
layoutComplete();
});
}
// Bonus one I've found helpful, once layout is finished
void layoutComplete() {
log("onActivityCreated / viewDidLoad / layoutComplete");
}
void viewWillAppear() {
log("onResume / viewWillAppear / onFocusGained");
}
void viewWillDisappear() {
log("onPause / viewWillDisappear / onFocusLost");
}
@override
void dispose() {
log("onDestroy / viewDidUnload / dispose");
super.dispose();
}
@override
Widget build(BuildContext context) {
return FocusDetector(
onFocusGained: viewWillAppear,
onFocusLost: viewWillDisappear,
child: Text('Rest of my widget'),
);
}
}
Other options
RouteObserver (Flutter)
-
didPush
=onResume
current screen is pushed on -
didPopNext
=onResume
current screen is being navigated back to -
didPop
=onPause
dismissing current page / going back -
didPushNext
=onPause
navigating forward to a new page -
Cons:
- Doesn’t cover use-case (2), backgrounding then foregrounding the app
WidgetsBindingObserver (Flutter)
-
AppLifecycleState.resumed
= The app is visible and responding to user input -
AppLifecycleState.paused
= The app is not visible and not responding to user input -
Cons:
- Isn’t widget/route specific (for external nav (2))
- Doesn’t cover use-case (1), navigating between pages
VisibilityDetector (Google)
-
onVisibilityChanged
(visibility == 1) =onResume
-
onVisibilityChanged
(visibility == 0) =onPause
-
Cons:
- Doesn’t cover use-case (2), backgrounding then foregrounding the app
Comments
-
Kyle Venn over 1 year
I've seen this asked before, but the questions are always subsets of functionality. I'm looking for exact equivalent life cycle events. And I can be more specific.
The ones I'm aware that exist in Flutter widgets are
onCreate
/viewDidLoad
=initState
onDestroy
/viewDidUnload
=dispose
These are the life cycle events I'd like to know about (for the currently visible widget/route, not the entire app):
onResume
/viewWillAppear
getting fired on the currently visible widget whenInternal nav (navigating around within the app)
a. You push the route/widget onto the stack
b. You navigate back to the route/widget (that was already on the back of nav stack)
External nav (backgrounding and foregrounding the app)
a. You open the app from the background (and the route/widget is the one being displayed to the user)
b. You turn the screen on (power button) and the app was already open
onPause
/viewDidDisappear
getting fired on the currently visible widget whenInternal nav (navigating around within the app)
a. You navigate away from a widget/route going forward
b. You navigate away from the widget by dismissing it / going backward in the stack
External nav (backgrounding and foregrounding the app)
a. You background the app (and the route/widget is the one being displayed to the user) b. You turn the screen off (power button) with the app open
-
Kyle Venn about 2 yearsYou can improve the
updateInterval
issue by callingVisibilityDetectorController.instance.updateInterval = 250;
on app start.