onResume() and onPause() for widgets on Flutter

Viewed 42540

Right now, a widget only has initeState() that gets triggered the very first time a widget is created, and dispose(), which gets triggered when the widget is destroyed. Is there a method to detect when a widget comes back to the foreground? and when a widget is about to go to the background because another widget just was foregrounded? It's the equivalent of onResume and onPause being triggered for Android, and viewWillAppear and viewWillDisappear for ios

6 Answers

This is a full example demonstrating how to properly handle things, to test this, press home button and resume the app, you shall see didChangeAppLifecycleState is getting called.

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();

    // Add the observer. 
    WidgetsBinding.instance!.addObserver(this);
  }

  @override
  void dispose() {
    // Remove the observer
    WidgetsBinding.instance!.removeObserver(this);

    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);

    // These are the callbacks
    switch (state) {
      case AppLifecycleState.resumed:
        // widget is resumed
        break;
      case AppLifecycleState.inactive:
        // widget is inactive
        break;
      case AppLifecycleState.paused:
        // widget is paused
        break;
      case AppLifecycleState.detached:
        // widget is detached
        break;
    }
  }

  @override
  Widget build(BuildContext context) => Scaffold();
}

I am a little late but came with perfect solution for those who may be looking for it in the future. the Navigator.push() is actually a Future. it means it has then() callback function. so the then() will be called after you call Navigator.pop() from the second screen. and even you can send some data from the second screen and access the data in the first screen.

example:

//from Screen A
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>B()))
.then((value)=>{ refresh() });

//in Screen B with data
Navigator.pop(context,[1]);

//or without data
Navigator.pop(context);

so refresh() will be called on resume of Screen A.

I created visibility_aware_state for having something that behaves similar to Android's Activity.onResume(). It also considers pop and push navigation.

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends VisibilityAwareState<Example> {
  @override
  Widget build(BuildContext context) {
    // return your widget
  }

  @override
  void onVisibilityChanged(WidgetVisibility visibility) {
    switch(visibility) {
      case WidgetVisibility.VISIBLE:
        // Like Android's Activity.onResume()
        break;
      case WidgetVisibility.INVISIBLE:
        // Like Android's Activity.onPause()
        break;
      case WidgetVisibility.GONE:
        // Like Android's Activity.onDestroy()
        break;
    }
    super.onVisibilityChanged(visibility);
  }
}

Mamnarock your answer is correct but not complete, and the link that you are shared isn't available.

Here is the complete code:

import 'package:flutter/material.dart';

class YourClass extends StatefulWidget {

  @override
  _YourClassState createState() => _YourClassState();
}

class _YourClassState extends State<YourClass>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        // Handle this case
        break;
      case AppLifecycleState.inactive:
        // Handle this case
        break;
      case AppLifecycleState.paused:
        // Handle this case
        break;
      case AppLifecycleState.detached:
        // Handle this case
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

And as TeeTracker mentioned in the comment:

This is an app-level lifecycle, which means when the whole is resumed or inactive, or paused, not the single widget.

Related