how change the play IconButton when the audio is Completed playing - android

how to make the IconButton icon change when the audio has finished playing

I am showing a simple code which mocks a music being played for 10 seconds and then stops after that and Icon changes
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: SOM());
/*
return MaterialApp(initialRoute: 'home', routes: <String, WidgetBuilder>{
'home': (context) => SOMain(),
'/secondPage': (context) => DefaultScaffold("Second Screen", SOSecond()),
'/thirdPage': (context) => DefaultScaffold("Third Screen", SOThird()),
});
*/
}
}
class SOM extends StatefulWidget {
#override
_SOMState createState() => _SOMState();
}
class _SOMState extends State<SOM> {
bool isPlaying = false;
IconData musicIcon = Icons.play_arrow;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: IconButton(
icon: Icon(musicIcon),
onPressed: toggleMusic,
)),
);
}
void toggleMusic() {
if (!isPlaying) {
startPlayingMusic();
}
}
void startPlayingMusic() {
setState(() {
isPlaying = true;
musicIcon = Icons.pause;
});
// faking music play for 10 seconds
Timer(Duration(seconds: 10), () {
setState(() {
isPlaying = false;
musicIcon = Icons.play_arrow;
});
});
}
}
Note: The current working does not change states if music is already playing, it just play mocks music play for 10s after button click and ends. Nothing else.

Create PlayerEvents like this:
enum PlayerEvent {
onStartPlaying, onStopPlaying, onPausePlaying
}
And create a BLoC like this:
final playerBloc = PlayerBloc();
class PlayerBloc {
StreamController<PlayerEvent> _playerEventStreamController = StreamController.broadcast();
Stream<PlayerEvent> get playerEventStream => _playerEventStreamController.stream;
dispatch(PlayerEvent event) {
_playerEventStreamController.sink.add(event);
}
dispose(){
_playerEventStreamController.close();
}
}
Wrap your icon with stream builder like this:
StreamBuilder<PlayerEvent>(
stream: playerBloc.playerEventStream,
initialData: PlayerEvent.onStopPlaying,
builder: (context, snapshot) {
switch(snapshot.data) {
case PlayerEvent.onStopPlaying:
return Icon(Icons.play_arrow);
case PlayerEvent.onStartPlaying:
return Icon(Icons.pause);
case PlayerEvent.onPausePlaying:
return Icon(Icons.play_arrow);
}
}
),
Now whenever your music starts or stops playing just call this:
playerBloc.dispatch(PlayerEvent.onStartPlaying);
or
playerBloc.dispatch(PlayerEvent.onStopPlaying);
and your icons will change as per your music.

Related

FlutterError (setState() called after dispose(): _welcomeScreen_1State#a1185(lifecycle state: defunct, not mounted) Error

I have an application. I added an introductory screen to this application that only appears at the entrance. This screen will appear when the user first opens the application. The next time they open, the promotional screen will not open again.
I've done it with Secure Storage and GetX whether the user has passed the promotion or not.
main.dart:
import 'package:flutter/material.dart';
import 'package:teen_browser/pages/home.dart';
import 'package:teen_browser/welcome_screens/welcomeMain.dart';
import 'package:get/get.dart';
import 'features/controllers/pagination-controller/pagination_controller.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
var skipStatus;
void main() {
runApp(mainApp());
}
class mainApp extends StatefulWidget {
mainApp({Key? key}) : super(key: key);
#override
State<mainApp> createState() => _mainAppState();
}
class _mainAppState extends State<mainApp> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: 'Montserrat Regular'),
home: passedTheWelcomeScreen(),
);
}
}
class passedTheWelcomeScreen extends StatefulWidget {
passedTheWelcomeScreen({Key? key}) : super(key: key);
#override
State<passedTheWelcomeScreen> createState() => _passedTheWelcomeScreenState();
}
class _passedTheWelcomeScreenState extends State<passedTheWelcomeScreen> {
final PaginationController _paginationController = Get.put(PaginationController());
#override
Widget build(BuildContext context) {
return GetX<PaginationController>(
init: _paginationController,
initState: (initController) {
initController.controller!.CheckSkipStatus();
},
builder: (controller) {
if (controller.isSkipped.value == null) {
return CircularProgressIndicator();
} else if (controller.isSkipped.value == true){
return HomeApp();
} else {
return welcomeMain();
}
},
);
}
}
pagination_controller.dart:
import 'package:get/get.dart';
import 'package:teen_browser/functions/secure_storage.dart';
class PaginationController extends GetxController {
RxnBool isSkipped = RxnBool(false);
void CheckSkipStatus() async {
final resp = await UserSecureStorage.isSkip();
isSkipped.value = resp;
}
}
secure_storage.dart:
import 'dart:developer';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class UserSecureStorage {
static const _storage = FlutterSecureStorage();
static Future setField(String key, value) async {
await _storage.write(key: key, value: value);
}
static Future<String?> getField(key) async {
return await _storage.read(key: key);
}
static Future deleteField(String key) async {
return await _storage.delete(key: key);
}
static Future deleteAll() async {
return await _storage.deleteAll();
}
static Future<bool> isSkip() async {
inspect(await getField("isSkipped"));
var value = await getField('isSkipped');
if (value != null) return true;
inspect(value);
return false;
}
}
On the last promotion page, I stated that the user passed the definition as follows:
await UserSecureStorage.setField("isSkipped", "true");
controller.isSkipped.value = true;
If the user has not passed the promotion before, there is no problem. All promotional screens are coming and error etc. does not give. But after passing all the demo screens, that is, when the value of isSkipped is true, it gives an error.
The error I got:
FlutterError (setState() called after dispose(): _welcomeScreen_1State#7c0ca(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)
I think the error is caused by the initState on the first demo page, which also shows the first demo page, welcomeMain1.dart, on the console.
welcomeScreen1.dart initState codes:
void initState() {
super.initState();
print(_clickGoButtonErrorMessages[Random().nextInt(_clickGoButtonErrorMessages.length)]);
if (controller.isSkipped.value == false) {
Timer(const Duration(milliseconds: 3200), () {
setState(() {
_helloText = 0;
});
});
Timer(const Duration(milliseconds: 3300), () {
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/StopBroDontPassSound.mp3"),
autoStart: true,
showNotification: true,
);
});
Timer(const Duration(milliseconds: 3400), () {
setState(() {
_helloTextState = true;
});
});
Timer(const Duration(milliseconds: 3500), () {
setState(() {
_helloTextState = false;
});
});
Timer(const Duration(milliseconds: 3900), () {
setState(() {
_helloText = 1;
});
});
Timer(const Duration(milliseconds: 4200), () {
setState(() {
_helloTextState = true;
});
});
Timer(const Duration(milliseconds: 5700), () {
setState(() {
_contentText = true;
});
});
Timer(const Duration(milliseconds: 7000), () {
setState(() {
_letsGoButton = true;
});
});
Timer(const Duration(milliseconds: 7300), () {
setState(() {
_skipButton = true;
});
});
Timer(const Duration(milliseconds: 9000), () {
setState(() {
_clickGoButton = true;
_clickSkipButton = true;
});
});
}
else {
inspect("isSkipped true");
}
}
Once the user introduction has passed, it will always be redirected to the homepage, ie HomeApp. If the value of isSkipped is false, it means that the user first opened the application. This time, it directs you to the promotional screens.
My guess is that the application is trying to run the initState codes in welcomeMain1.dart while redirecting to HomeApp() and throws an error when it doesn't.
I hope I was able to explain my problem.When the user promotion passes, the value of isSkipped is true, ie "user promotion passed". I tested it by printing it to the console. How can I solve this problem?
Thanks for help.
There could be a race condition so that isSkipped is false long enough for the timers to start, then it switches to true and the widget is rebuilt but the timers are still running.
You could add this before setState in your timer callbacks:
Timer(const Duration(milliseconds: 3200), () {
if (!mounted) {
return;
}
setState(() {
_helloText = 0;
});
});
You could also save all timers as individual properties and cancel them in the widget's dispose method:
class MyWidget extends StatelessWidget
{
late Timer _timer1;
late Timer _timer2;
void initState() {
super.initState();
if (controller.isSkipped.value == false) {
_timer1 = Timer(const Duration(milliseconds: 3200), () {
setState(() {
_helloText = 0;
});
});
_timer2 = ...
}
// ...
}
#override
void dispose() {
_timer2.cancel();
_timer1.cancel();
super.dispose();
}
}
(The above is dry-coded, sorry for any errors. Storing the timers in an array is left as an exercise to the reader.)

How can we play video in Alert Dialogue Flutter

To play video within the alert dialog I am using the below code. But Video is not playing. Is it possible to play video within the Alert Dialog? Does anyone has a solution to this please share it with me.
Future<void> _initializeVideoPlayerFuture;
VideoPlayerController _controller;
_controller = VideoPlayerController.network(
video_base_url+videoUrl,
);
// Initialize the controller and store the Future for later use.
_initializeVideoPlayerFuture = _controller.initialize();
// Use the controller to loop the video.
_controller.setLooping(true);
// If the video is playing, pause it.
if (_controller.value.isPlaying) {
_controller.pause();
} else {
// If the video is paused, play it.
_controller.play();
}
ContainerResponsive(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width-50,
height: 150,
child: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_controller),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return const Center(
child: CircularProgressIndicator(),
);
}
},
)
),
Make a new Stateful widget class like this for video player , initialized video player controller and video player widget here
import 'package:approved/src/constants/colors.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:video_player/video_player.dart';
class VideoWidget extends StatefulWidget {
final String? url;
final bool? play;
const VideoWidget({#required this.url, #required this.play});
#override
_VideoWidgetState createState() => _VideoWidgetState();
}
class _VideoWidgetState extends State<VideoWidget> {
late VideoPlayerController _controller;
Future<void>? _initializeVideoPlayerFuture;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.network(widget.url!);
_initializeVideoPlayerFuture = _controller.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
if (widget.play!) {
_controller.play();
_controller.setLooping(true);
}
}
#override
void didUpdateWidget(VideoWidget oldWidget) {
if (oldWidget.play != widget.play) {
if (widget.play!) {
_controller.play();
_controller.setLooping(true);
} else {
_controller.pause();
}
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(children: [
Container(
color: Colors.white,
child: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return VideoPlayer(_controller);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
),
]);
}
}
Now you can use this widget anywhere you have to just pass the video url and playing parameter true/false.
Like I am using in showDialog method
playVideo(
BuildContext context,
) {
return showDialog(
context: context,
builder: (context) {
return Center(
child: SizedBox(
height: 300,
width: 300,
child: VideoWidget(url: url, play: true)));
});
}

How do I make the build function wait until a button is pressed in an alert in init?

I am trying to display an Alert that shows a disclaimer to the user as soon as the app is opened. The build method will run, that is the app will start its processing only after the user presses okay on the alert.
I've managed to show the alert in init using
SchedulerBinding.instance.addPostFrameCallback((_) => AlertWindow().showAlert(context));
or
Future.delayed(Duration.zero, () => AlertWindows().showAlert(context));
This shows the alert, but the app starts building in the background. I want the app to run/build only after OKAY button is pressed, and after the alert is popped.
Hey I implemented some code, you can try this code directly on dartPad Paste the code in this Editor
I used setState, if it is for real time project you can use Providers or bloc, for performance.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Widget viewHolder;
void initState() {
viewHolder = Container();
WidgetsBinding.instance
.addPostFrameCallback((_) => afterPostFrameCallBack());
super.initState();
}
afterPostFrameCallBack() {
_showDialog();
}
#override
Widget build(BuildContext context) {
return viewHolder;
}
Widget _buildView() {
return Container(child: Text('This is after okay button'));
}
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: Text("App Update Available"),
content: Text(
"We have fixed some issues and added some cool features in this update"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
FlatButton(
child: new Text("ok"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
viewHolder = _buildView();
});
},
),
],
);
},
);
}
}

How to pause flutter video(video_player plugin) when navigating from page to another

I'm using flutter video_player(https://pub.dartlang.org/packages/video_player) plugin to play videos. But When I navigate from one page to another in flutter the video is still playing. I'm having the same problem when playing music from any other app(eg: playing music from notification while flutter video is playing). How can I pause it?
Edit:
Second option:
So I came up with another solution. VisibilityDetector
is a `Widget' that can wrap any other Widget and notify when the visible area of the widget changed.
VisibilityDetector(
key: Key("unique key"),
onVisibilityChanged: (VisibilityInfo info) {
debugPrint("${info.visibleFraction} of my widget is visible");
if(info.visibleFraction == 0){
videoPlayerController.pause();
}
else{
videoPlayerController.play();
}
},
child: VideoPlayer());
)
First Option:
By using the RouteObserver and RouteAware your widget will be informed whenever another screen is pushed on top of it, or it's popped back on top.
First, you need to add a RouteObserver to MaterialApp or your Navigator.
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorObservers: [routeObserver], //HERE
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Then you must add the RouteAware mixin to your widget, and subscribe it to the RouteObserver.
class VideoPlayerItem extends StatefulWidget {
final File file;
VideoPlayerItem(this.file);
#override
_VideoPlayerItemState createState() => _VideoPlayerItemState();
}
class _VideoPlayerItemState extends State<VideoPlayerItem> with RouteAware {
VideoPlayerController _videoPlayerController;
#override
void initState() {
super.initState();
_videoPlayerController = VideoPlayerController.file(widget.file);
_videoPlayerController.initialize().then((_) {
if (mounted) {
setState(() {});
_videoPlayerController.play();
}
});
}
#override
void didChangeDependencies() {
routeObserver.subscribe(this, ModalRoute.of(context));//Subscribe it here
super.didChangeDependencies();
}
/// Called when the current route has been popped off.
#override
void didPop() {
print("didPop");
super.didPop();
}
/// Called when the top route has been popped off, and the current route
/// shows up.
#override
void didPopNext() {
print("didPopNext");
_videoPlayerController.play();
super.didPopNext();
}
/// Called when the current route has been pushed.
#override
void didPush() {
print("didPush");
super.didPush();
}
/// Called when a new route has been pushed, and the current route is no
/// longer visible.
#override
void didPushNext() {
print("didPushNext");
_videoPlayerController.pause();
super.didPushNext();
}
#override
Widget build(BuildContext context) {
return _videoPlayerController?.value?.initialized ?? false
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
)
: Container();
}
#override
void dispose() {
routeObserver.unsubscribe(this); //Don't forget to unsubscribe it!!!!!!
super.dispose();
_videoPlayerController.dispose();
}
}
You have - VideoPlayerController _controller;
Before Navigating to other screen -
_controller.pause(); // add this
Navigator.push(
context, MaterialPageRoute(builder: (BuildContext context) => NextPage()));
OR
if it's a Stateful widget then you can override - dispose() method.
#override
void dispose() {
super.dispose();
_controller.pause();
}
Simple and its work for me, I get this from chewie example code: https://pub.dev/packages/chewie/example
Just on the onTap/onPressed use setState and in this setState make videoController.pause or dispose, whatever you want.
onTap:() {
setState(() {
widget.videoController.pause();
Navigator.of(context)
.pushNamed(YOUR-NAVIGATION-PAGE-NAME);
});
},

onResume() and onPause() for widgets on Flutter

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
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;
}
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();
}
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 States 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,
),
],
),
);
}
}
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.

Categories

Resources