how to store starting and ending time of a timer using shared preference in flutter and when i close the app and reopen app should start from ending timer time
if anyone knows anything regarding this issue please share
By using WidgetsBindingObserver you can listen to your application state.
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> 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) {
super.didChangeAppLifecycleState(state);
// Here you will get your application state like resumed, inactive, paused, detached
// when the application will close `detached` state will come, and store the latest timer data in your shared preferences
if(state == AppLifecycleState.detached){
// store last timer data into shared preferences
}
print('Current state = $state');
}
#override
Widget build(BuildContext context) => Scaffold();
}
and when your application starts :
Get the data from Shared preferences and Start the timer from there
Do this thing after initializing the shared preferences in main.dart or in your root widget.
Related
I am building a QR code scanner app with a couple of tabs wrapped up within CupertinoTabBar in a CupertinoTabScaffold. I have a CupertinoTabController to take care of the switching between the tabs. One of this tabs has a CameraPreview widget from the Camera plugin of Flutter along with a proper dispose mechanism. However, whenever the tab are switched, the Camera stream still persists, causing the phone to heat up and also causes janky UX. Now I read that the BottomNavigationBar from Material widgets does not persist in this way. Any idea on how to achieve the same behaviour with CupertinoTabBar?
You can use the StatefulWidget for each a page of the tabs and then try to listening AppLifecycleState. Disponse controller if state inactive/paused.
In my case it's working fine.
class Example extends StatefulWidget {
#override
ExampleState createState() => ExampleState();
}
//Implement WidgetsBindingObserver to listen Lifecycle State
class ExampleState extends State<Example> with WidgetsBindingObserver {
late CameraController _controller;
...
...
#override
void initState() {
super.initState();
// Add Listener (Lifecycle State)
WidgetsBinding.instance!.addObserver(this);
}
Future<void> _setupController() async {
//todo setup/init controller
}
//Implements this method to listen Lifecycle State
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_controller.dispose();
_setupCameraAndControllerFuture = _setupController();
}
if (state == AppLifecycleState.inactive) {
_controller.dispose();
} else if (state == AppLifecycleState.paused) {
_controller.dispose();
}
}
#override
void dispose() {
// Remove Listener (Lifecycle State)
WidgetsBinding.instance!.removeObserver(this);
// dispose controller
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
...
);
}
}
I am using Flutter with hooks and I am trying to get the App Life Cycle State. I followed documentation and created new hook (code shown below) which works ok for all situations with one exception. When the application state becomes "paused", the hook does not return the value back to the widget. I am not clear what to do at this point. Someone suggested using Isolates but I don't see how that can help. Updating App Life Cycle is not compute expensive.
Please let me know what else I could do make this work.
Thanks
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
AppLifecycleState useAppLifecycleState() {
return use(const _LifeCycleState());
}
class _LifeCycleState extends Hook<AppLifecycleState> {
const _LifeCycleState();
#override
__LifeCycleState createState() => __LifeCycleState();
}
class __LifeCycleState extends HookState<AppLifecycleState, _LifeCycleState>
with WidgetsBindingObserver {
AppLifecycleState _state;
#override
void initHook() {
super.initHook();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
#override
AppLifecycleState build(BuildContext context) {
return _state;
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_state = state;
});
super.didChangeAppLifecycleState(state);
}
}
Thanks for your help.
In my app, I am playing music (local) in a loop, which plays continuously unless the user stops it. I am using audioplayers package.
Future playLoop(String filePath) async {
player.stop();
player = await cache.loop(filePath);
}
Currently, when app is minimised, the music is not getting stoped. The feature I want to implement is that when the app is minimised, it should stop playing music in the background.
Thanks in advance.
Solutions :
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
//stop your audio player
}else{
print(state.toString());
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
There are mainly 4 states for it:
resumed: The application is visible and responding to user input.
inactive: The application is in an inactive state and is not receiving
user input.
paused: The application is not currently visible to the user, not
responding user input, and running in the background.
detached: The application is still hosted on a flutter engine but is
detached from any host views.
The solution above is correct, but some steps are needed before to get it
1 add WidgetsBindingObserver to your class
class AnyClass extends StatefulWidgets {
_AnyClassState createState() => _AnyClassState();
}
class _AnyClassState extends State<AnyClass> with
WidgetsBindingObserver {
Widget build(BuildContext context) {
return ...
}
}
2 Now it will work, we can added the methods inside class
class _AnyClassState extends State<AnyClass> with
WidgetsBindingObserver {
// ADD THIS AppLifecycleState VARIABLE
late AppLifecycleState appLifecycle;
// ADD THIS FUNCTION WITH A AppLifecycleState PARAMETER
didChangeAppLifecycleState(AppLifecycleState state) {
appLifecycle = state;
setStae(() {});
if(state == AppLifecycle.paused) {
// IF YOUT APP IS IN BACKGROUND...
// YOU CAN ADDED THE ACTION HERE
print('My app is in background');
}
}
// CREATE INITSTATE AND DISPOSE METHODS
initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) {
return ...
}
}
NOW IT WILL WORK FINE!
I am using WidgetsBindingObserver to check the platform brightness, it works well when the app is up and running. The app can detect the platform brightness changes by user. However when the app is closed and reopen again. The initial startup the method "didChangePlatformBrightness()" did not run at all for Android but it was executed for IOS. Why is this so?
Below is my code:
class _MyAppState extends State<MyApp> with WidgetsBindingObserver{
ScaffoldDataBloc bloc;
ScaffoldDataState state;
var systemBrightness;
#override
void initState() {
WidgetsBinding.instance.addObserver(this);
bloc = ScaffoldDataBloc();
}
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
#override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
final Brightness brightness = WidgetsBinding.instance.window.platformBrightness;
//inform listeners and rebuild widget tree
systemBrightness = brightness;
print('didchange: ' + systemBrightness.toString());
setState(() {});
}
....
On Android, the print statement 'didchange' was not printout. For IOS this is the result:
flutter: didchange: Brightness.dark
Is it a bug?
Thanks.
I'm trying to share same bloc across two routes.
But when I come back from second route the bloc get automatically disposed so in the first route I find myself with all the stream closed.
For example this is the first route (HomePage) where I instantiate the bloc, download a list from api and show it in the build method.
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
GroupsBloc _groupBloc;
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies(){
super.didChangeDependencies();
_groupBloc = GroupsBloc();
_groupBloc.getAll();
}
#override
void dispose(){
_groupBloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
...
}
}
Then I navigate to a second screen where I can add an item to the list.
_onAddGroupPress(){
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => BlocProvider<GroupsBloc>(bloc: _groupBloc, child: GroupPage()),
fullscreenDialog: true
),
);
}
In the second screen I retrieve the bloc and I use it to add an item, then I go back to Home Page.
class GroupPage extends StatefulWidget {
#override
_GroupPageState createState() => _GroupPageState();
}
class _GroupPageState extends State<GroupPage> {
FormBloc _formBloc; //another bloc
GroupsBloc _groupBloc;
#override
void initState(){
super.initState();
}
#override
void didChangeDependencies(){
super.didChangeDependencies();
_formBloc = FormBloc();
_groupBloc = BlocProvider.of<GroupsBloc>(context); //retrieve of the bloc
}
#override
void dispose() {
_formBloc?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
...
}
In the dispose method of the second screen I dispose only _formBloc bloc but _groupBloc gets disposed too, so when I come back in the first page I found myself with _groupBloc disposed and all it's stream closes.
I tought of passing the bloc to the second screen as a props but I don't know if it's the right way to do this.
It obviously depends on the scope of your Bloc, but there is nothing preventing you from sharing the same instance throughout your whole app.
ie. simply wrap your whole MaterialApp inside BlocProvider<GroupsBloc>(bloc: _groupBloc, child: MaterialApp( ... ))
if "groups" are not global to your app, you should probably just pass the bloc along to the second widget.