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

1

9

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

Kaule answered 26/2, 2022 at 19:53 Comment(0)
K
25

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
Kaule answered 26/2, 2022 at 19:53 Comment(5)
You can improve the updateInterval issue by calling VisibilityDetectorController.instance.updateInterval = 250; on app start.Kaule
A great write-up and looks like a great solution, but not compatible with latest version of Google mobile ads. Receive error: 'Because focus_detector >=2.0.1 depends on visibility_detector ^0.2.2 and google_mobile_ads 2.2.0 depends on visibility_detector ^0.3.3, focus_detector >=2.0.1 is incompatible with google_mobile_adsPoach
Have you tried dependency override? dart.dev/tools/pub/dependencies#dependency-overrides "dependency_overrides: visibility_detector: ^0.3.3" If that doesn't work, this should: github.com/EdsonBueno/focus_detector/issues/…Kaule
This FocusDetectoris a great solution, it is terrible that these methods aren't part of the framework. I'm even wondering how people are developing apps without these.Owensby
Thanks for this meticulous answer, so many people don't even read the question to tell it is use-case-1 or use-case-2, and they just give solutions right away!Kenosis

© 2022 - 2024 — McMap. All rights reserved.