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
-
What is your use case? Knowing what you are trying to do can help us provide more info. Thanks! – Seth Ladd Jun 09 '17 at 21:21
-
The answer is awesome! But what I looked for exactly was answered in https://stackoverflow.com/a/44417260/3217522 – user3217522 Jun 10 '17 at 00:26
-
1Have a look at FocusDetector. It is listening the widget's foreground visibility: https://pub.dev/packages/focus_detector – JustADeveloper Dec 11 '20 at 11:35
6 Answers
There is an abstract class caller WidgetsBindingObserver
https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html
in
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_notification = state;
});
}
there is the "state", can be manage as
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.suspending:
// Handle this case
break;
}
-
1I tried this but the didChangeAppLifecycleState() is never called. what else can I do? – CanCoder Jan 13 '20 at 02:45
-
1Did you add the "with WidgetsBindingObserver"? see the documentation at https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html – Mamnarock Jan 14 '20 at 03:30
-
thanks. I later realised how lifecycle works. it is not meant to solved the issue I wanted to solve. I have found another way to solve the problem. – CanCoder Jan 14 '20 at 22:35
-
35this is app-level lifecycle, which means, when the whole is resumed or inactive or paused, not the single widget. – TeeTracker May 10 '20 at 12:34
-
-
3
-
Newbies or Freshers can't understand this solution. They must have check the below answer. – Pratik Butani May 06 '22 at 12:19
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();
}

- 237,138
- 77
- 654
- 440
-
4
-
6Actually this code snippet monitor the application life cycle and not the widget. If you test it you'll see that the "first" on resume isn't called. This is because the app is already resumed. Need to find another solution for the Android fragment/Activity equivalent :) – user3193413 Nov 19 '21 at 10:09
-
as @TeeTracker mentioned, this is app-level lifecycle, which means, when the whole is resumed or inactive or paused, not the single widget. – 3ameration Sep 02 '22 at 20:13
The most common case where you'd want to do this is if you have an animation running and you don't want to consume resources in the background. In that case, you should extend your State
with TickerProviderStateMixin
and use your State
as the vsync
argument for the AnimationController
. Flutter will take care of only calling the animation controller's listeners when your State
is visible.
If you want the State
s that live in your PageRoute
to be disposed when the PageRoute
is obscured by other content, you can pass a maintainState
argument of false
to your PageRoute
constructor. If you do this, your State
will reset itself (and its children) when it's hidden and will have to re-construct itself in initState
using the properties passed in as constructor arguments to its widget
. You can use a model or controller class, or PageStorage
, to hold the user's progress information if you don't want a complete reset.
Here is a sample app that demonstrates these concepts.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/') {
return new MaterialPageRoute<Null>(
settings: settings,
builder: (_) => new MyApp(),
maintainState: false,
);
}
return null;
}
));
}
class MyApp extends StatefulWidget {
MyAppState createState() => new MyAppState();
}
class MyAppState extends State<MyApp> with TickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
print("initState was called");
_controller = new AnimationController(vsync: this)
..repeat(min: 0.0, max: 1.0, period: const Duration(seconds: 1))
..addListener(() {
print('animation value ${_controller.value}');
});
super.initState();
}
@override
void dispose() {
print("dispose was called");
_controller.dispose();
super.dispose();
}
int _counter = 0;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('home screen')
),
body: new Center(
child: new RaisedButton(
onPressed: () {
setState(() {
_counter++;
});
},
child: new Text('Button pressed $_counter times'),
),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.remove_red_eye),
onPressed: () {
Navigator.push(context, new MaterialPageRoute(
builder: (BuildContext context) {
return new MySecondPage(counter: _counter);
},
));
},
),
);
}
}
class MySecondPage extends StatelessWidget {
MySecondPage({ this.counter });
final int counter;
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Certificate of achievement'),
),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
new Icon(Icons.developer_mode, size: 200.0),
new Text(
'Congrats, you clicked $counter times.',
style: Theme.of(context).textTheme.title,
textAlign: TextAlign.center,
),
new Text(
'All your progress has now been lost.',
style: Theme.of(context).textTheme.subhead,
textAlign: TextAlign.center,
),
],
),
);
}
}

- 110,240
- 31
- 221
- 152
-
What if it's just one tab of a tab controller that you want to dispose/recreate? And you don't want to mess with other tabs that's going to be lot of pain to save/restore? – Anton Duzenko Sep 28 '21 at 12:59
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.

- 729
- 1
- 6
- 15
-
What if Screen B does not have `Navigator.pop(context,[1]);`. So the `then` on Screen A will never run. – Ben Mar 23 '21 at 05:30
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);
}
}

- 1,826
- 2
- 22
- 26
-
1Nice, thank you for building and sharing this solution. I have a base class setup, and the solution with `WidgetsBindingObserver` and `@override` for `void didChangeAppLifecycleState(AppLifecycleState state)` was not working there. This does work in that setup – Gene Bo Sep 05 '21 at 20:33
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.

- 1,492
- 2
- 16
- 28
-
Thanks, @Abbas, it is working for me. Any suggestion for screen level lifecycle in the same way? – Suresh Apr 10 '23 at 07:31
-