Does the WidgetsBindingObserver work on a stateless widget? - flutter

I am trying to use the WidgetsBindingObserver to see if my app is brought to the foreground. But it doesn't seem do anything. Does it only work on a statefull widget?
class TheHomeView extends StatelessWidget with WidgetsBindingObserver {
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
print('lifecycle changed');
if (state == AppLifecycleState.resumed) {
print('resumed');
showLatestGroupNotification();
}
}

Well it turns out, you can use it in a stateless widget. But you need to use
WidgetsBinding.instance.addObserver(this);
which you can do in the constructor of the widget. But if you want to remove the binding on dispose
WidgetsBinding.instance.removeObserver(this);
You would need a dispose which is only available in a statefull widget. Or you would have to do it manually.
https://dev.to/pedromassango/onresume-and-onpause-for-widgets-on-flutter-27k2

Related

How to use dispose with flutter bloc?

I have this stateful widget which uses a bloc called RecorderBloc:
class _RecorderScreenWidgetState extends State<RecorderScreenWidget> {
late final RecorderBloc _recorderBloc;
#override
void initState() {
super.initState();
_recorderBloc = serviceLocator.get<RecorderBloc>();
}
#override
void dispose() {
_recorderBloc.add(RecorderEvent.dispose());
super.dispose();
}
#override
Widget build(BuildContext context) {
//.....ommitted code
}
As you can see I need to dispose some members of the bloc after I finish from them, and that is done by adding a dispose event.
But I don't know if defining the bloc as a member variable of the stateful widget is the right approach?
If not, then how can I get the instance of the bloc inside the dispose() method of the StatefulWidget to add a dispose event?
As far as I know there is no need for defining a dispose event. The Bloc class has a close function which will be called when the provider widget (BlocProvider) state is being disposed. You can override that function inside of your BLoC and do whatever is needed.
class MyBloc extends Bloc<MyBlocState> {
#override
Future<void> close() {
// dispose
return super.close();
}
}

Flutter: Provider and how to update records from the DB in the background

I am new to Flutter and I have this simple use case: in my Cloud Firestore DB I have a list of JSON representing events. I want to show them through my Flutter app in a ListView.
My requirements is that the ListView doesn't refresh in real-time but only when a pull-on refresh (implemented using RefreshIndicator) is done by the user or when the app resumes from background
I tried to implement this in 2 ways (I am using provider package for state management):
Using StreamProvider to create a stream of records from the DB. This continuosly updates the list view (basically the widget changes while the user is looking at it and I don't want this)
Using a ChangeNotifierProvider that refers to a EventManager class which holds a List<Event>. This class has a pull method which updates its internal state. I call this method when the user does the pull-on refresh (in the onRefresh callback of RefreshIndicator).
Option 2 seems to work well however I do not know how to implement the refresh when the app resumes from background. As I said I am using provider (and therefore StatelessWidget) and apparently there is no way to bind to these events when using StatelessWidgets
Do you have any suggestions and best practices for this use case?
You need to access Flutters lifecycle methods and fire a callback when the app resumes.
You can add a stateful widget with WidgetsBindingObserver and put that somewhere in the scope of your Provider, but as a parent of whatever widget you use to display the info.
Or you can make your PullToRefresh widget stateful and do the same thing.
class LifeCycleWidget extends StatefulWidget {
#override
_LifeCycleWidgetState createState() => _LifeCycleWidgetState();
}
class _LifeCycleWidgetState extends State<LifeCycleWidget>
with WidgetsBindingObserver {
AppLifecycleState _appLifecycleState;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
refreshOnResume();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_appLifecycleState = state;
});
refreshOnResume();
}
void refreshOnResume() {
if (_appLifecycleState == AppLifecycleState.resumed) {
print('resumed');
// your refresh method here
}
}
#override
Widget build(BuildContext context) {
return HomePage();
}
}
Add the following to your main method if it's not there already.
WidgetsFlutterBinding.ensureInitialized();
Another way to do it without adding a stateful widget would be with GetX. You can still keep all your Provider stuff but only use the SuperController which provides lifecycle methods. This I can't test because I don't have your Provider code but you can probably get away with creating the class below and initializing the controller somewhere within the scope of the relevant Provider widget with
Get.put(LifeCycleController());
Then call the function in the onResumed override and you can use Get.context if you need context.
class LifeCycleController extends SuperController {
#override
void onDetached() {
debugPrint('on detached');
}
#override
void onInactive() {
debugPrint('on inactive');
}
#override
void onPaused() {
debugPrint('on pause');
}
#override
void onResumed() {
// your refresh function here. Access context with Get.context
debugPrint('on resume');
}
}

Flutter listen to lifecycle events on Stateless Widget?

Is there any solution for listening to lifecycle events for a StatelessWidget when the app is in background / foreground ? There is no StatefulWidget on that particular screen. I am using Getx() controller to observe data changes.
Use this code for stateless widget
SystemChannels.lifecycle.setMessageHandler((msg) async {
print('SystemChannels---> $msg');
});
For GetX architechture you dont have to use StatefulWidget but to listen lifecycle changes,
it seems like you have to use StatefulWidget.
Just override this method in a StatefulWidget.
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async
{
if (state == AppLifecycleState.paused) {}
else if (state == AppLifecycleState.resumed) {}
}
better use statefulwidgets for listen to lifecycle events

How to have access Context in didChangeAppLifecycleState lifecycle hook using flutter HookWidget?

I'm trying to access context so i can read my provider but since this lifecycle hook is out side the widget tree. it's not accessible. is there a way to get access to context?
I researched a little bit and finally discussed with narcodico from the flutter bloc community, so the credits are for him.
Therefore, mixin WidgetsBindingObserver on a state class, the context is available even in the overrides like didChangeAppLifecycleState since they are part of the state class.
Also, take in consideration to move to BlocProvider above the state widget.
Example
class HomePageProvider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<InAppPurchasesBloc>(),
child: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
...
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
context
.read<InAppPurchasesBloc>()
.add(const InAppPurchasesEvent.getPurchaserInfo());
}
}
...
}
I am afraid you can't access context inside didChangeAppLifecycleState.
For anyone interested, you can save your scaffold state in a global key, and access the context from its current state.
You can use useEffect function, read more:
https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useEffect.html;
Widget build(BuildContext context) {
useEffect(() {
//what would you write in initState
},
);
You may consider using the Riverpod package instead of Provider. Riverpod is from the same author as Provider and considered the "better Provider", but with many improvements including Flutter independence, meaning it does not rely on a context to work, and you can use it almost the same way as provider.
Using Riverpod, along with Flutter Hooks, you can do something like:
// create a provider in a global context
final myProvider = Provider((ref) => myClass());
// access the provider inside your class
class MyWidget extends HookWidget{
//access the provider using a hook
final myClassProvider = useProvider(myProvider);
//... your logic
#override
Widget build (BuildContext context){/* ... build widget tree... */}
}
Consider this very useful and concise tutorial with how to use Riverpod with Flutter Hooks and StateNotifier, ChangeNotifier, etc...

Should I dispose ValueNotifier object my self? Is it safe to leve it as it is?

I have a custom Stateless widget that has a ValueNotifier paramter as constructor paramter.
See the snippet below.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FlatChoiceChipList(
choiceList: ["first item","second item","third item"],
selectedIndex: ValueNotifier<int>(0),
onSelected: (index) {},
);
}
}
​
Well, as you can see in the above snippet, in the build method I created a ValueNotifier object and does not dispose it later.
Is it safe to create the ValueNotifier within the build method?
should I create the ValueNotifier in the parent Stateful Widget and dispose it myself when the parent Stateful widget got disposed??
1.You can if you want, but why don't you just declare it inside the your FlatChoiceChipList widget?
2.I believe only if you add listeners to it. This is the dispose method:
#mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_listeners = null;
}
But it's good practice to dispose when you don't need it anymore.