Flutter exact lifecycle equivalents to onResume / onPause on Android and viewWillAppear / viewDidDisappear on iOS

502

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 and WigetBindingObserver under the hood)
    • The default VisibilityDetectorController.updateInterval is 500ms which means the events get fired somewhat late.

Borrowing the style from one of my favorite posts:

iOS, Android, and Flutter Life Cycle

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
Share:
502
Kyle Venn
Author by

Kyle Venn

Experience in Java, Android, and Web Development.

Updated on January 03, 2023

Comments

  • Kyle Venn
    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 when
    1. Internal 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)

    2. 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 when
    1. Internal 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

    2. 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
    Kyle Venn about 2 years
    You can improve the updateInterval issue by calling VisibilityDetectorController.instance.updateInterval = 250; on app start.