Keep state of statefull widget that is inside a bloc builder - flutter

I have a statefull widget that is inside a bloc builder, my question is if there is anyway I can keep the state of the widget between rebuilds? Because every time the bloc builder is triggered the statefull widget state is being reseted.
Thank you!

You can use AutomaticKeepAliveClientMixin.
Use it with your state class.
You will have to override wantKeepAlive method in your state class.
return true in your case.
Example would be
class _FooWidgetState extends State<FooWidget> with AutomaticKeepAliveClientMixin {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
// your build method
}
#override
bool wantKeepAlive => true;
}

Related

How to move a variable outside build?

How do I move a variable that uses a context outside of the build method so that it is created once?
class _EventListState extends State<EventList> {
#override
Widget build(BuildContext context) {
final eventNotifier = EventInherited.of(context).eventNotifier;
...
You can use the late modifier.
class _EventListState extends State<EventList> {
late final eventNotifier = EventInherited.of(context).eventNotifier;
#override
Widget build(BuildContext context) {
...
make
eventNotifier
class variable and connect it to the getter function.
class _EventListState extends State<EventList> {
var? eventNotifier;
#override
Widget build(BuildContext context) {
eventNotifier = EventInherited.of(context).eventNotifier;
}
Dynamic get getEventNotifier => this.eventNotifier;
You can use the didChangeDependencies() method. The context is available at that point.
#override
void didChangeDependencies(){
super.didChangeDependencies();
final eventNotifier = EventInherited.of(context).eventNotifier;
}
This method will be called again in certain scenarios, so be careful about what you are initializing.
This thread has more information on when it is recalled, but in general it is okay to use for initialing listeners.
Understanding Flutter didChangeDependencies mechanics

dispose() is called when using AutomaticKeepAliveClientMixin

I am under the impression that using AutomaticKeepAliveClientMixin would prevent the states dispose() callback from being called when the Widget isn't visible anymore.
However, I have a situation where dispose() and initState() get called every time I hide/show a Widget, even though I implemented AutomaticKeepAliveClientMixin correctly.
class IdleScreenState extends State<IdleScreen> with AutomaticKeepAliveClientMixin {
#override
void initState() {
super.initState();
print('IdleScreen initState');
}
#override
void dispose() {
print('IdleScreen dispose');
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
// ...build the page...
}
#override
bool get wantKeepAlive => true;
}
This is how I hide/show this Widget
class MainScreen extends State<MainScreen> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return somecondition ? IdleScreen() : OtherScreen();
}
#override
bool get wantKeepAlive => true;
}
Every time this Widget (screen) is shown, initState()gets called, and every time I hide it, dispose() gets called. It's as if the AutomaticKeepAliveClientMixin has no effect. All other similar issues I could find seem to be due to either missing the wantKeepAlive => true or the super.build(context), but they are 100% there in the code.
I tried supplying a GlobalKey for IdleScreen as well, but that didn't have any effect.
However, if I use an IndexedStack or Offstage to hide/show the widget, it works as expected (initState() and dispose() don't get called when hiding/showing the widget).
IndexedStack(
index: somecondition ? 0 : 1,
children: [
IdleScreen(),
OtherScreen()
],
),
Maybe I'm mistaken, but isn't the whole purpose of AutomaticKeepAliveClientMixin to not have to manually keep the widget around using this technique?
This is in a web project, if that matters.
The type argument T is the type of the StatefulWidget subclass of the State into which this class is being mixed.
you have to pass the widget class name like this..
class IdleScreenState extends State<IdleScreen>
with AutomaticKeepAliveClientMixin <IdleScreen> {...

How should I implement the init method? In a stateful or stateless widget?

What is the rule of thumb to use an initial method for a widget. Shall I use the:
A. classical stateful widget approach?
Or is it better to stick with the B. stateless widget approach?
Both seem to work from my testing. In terms of code reduction, it seems the B. approach is better, shorter, cleaner, and more readable. How about the performance aspect? Anything else that I could be missing?
Initializing a controller should be a one-time operation; if you do it on a StatelessWidget's build method, it will be triggered every time this widget is rebuilt. If you do it on a StatefulWidget's initState, it will only be called once, when this object is inserted into the tree when the State is initialized.
I was looking for initializing some values based on values passed in constructor in Stateless Widget.
Because we all know for StatefulWidget we have initState() overridden callback to initialize certain values etc. But for Stateless Widget no option is given by default. If we do in build method, it will be called every time as the view update. So I am doing the below code. It works. Hope it will help someone.
import 'package:flutter/material.dart';
class Sample extends StatelessWidget {
final int number1;
final int number2;
factory Sample(int passNumber1, int passNumber2, Key key) {
int changeNumber2 = passNumber2 *
2; //any modification you need can be done, or else pass it as it is.
return Sample._(passNumber1, changeNumber2, key);
}
const Sample._(this.number1, this.number2, Key key) : super(key: key);
#override
Widget build(BuildContext context) {
return Text((number1 + number2).toString());
}
}
Everything either a function or something else in widget build will run whenever you do a hot reload or a page refreshes but with initState it will run once on start of the app or when you restart the app in your IDE for example in StatefulWidget widget you can use:
void initState() {
super.initState();
WidgetsBinding.instance!
.addPostFrameCallback((_) => your_function(context));
}
To use stateful functionalities such as initState(), dispose() you can use following code which will give you that freedom :)
class StatefulWrapper extends StatefulWidget {
final Function onInit;
final Function onDespose;
final Widget child;
const StatefulWrapper(
{super.key,
required this.onInit,
required this.onDespose,
required this.child});
#override
State<StatefulWrapper> createState() => _StatefulWrapperState();
}
class _StatefulWrapperState extends State<StatefulWrapper> {
#override
void initState() {
// ignore: unnecessary_null_comparison
if (widget.onInit != null) {
widget.onInit();
}
super.initState();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void dispose() {
if (widget.onDespose != null) {
widget.onDespose();
}
super.dispose();
}
}
Using above code you can make Stateful Wrapper which contains stateful widget's method.
To use Stateful Wrapper in our widget tree you can just wrap your widget with Stateful Wrapper and provide the methods or action you want to perform on init and on dispose.
Code available on Github
NOTE: You can always add or remove method from Stateful Wrapper Class according to your need!!
Happy Fluttering!!

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();
}
}

How to use ever() with Flutter getx variable in GetxController if I didn't add .obs

I am using Flutter getx package.
I know how to use "ever" with (.obs) variable like this
class CountController extends GetxController {
final count = 0.obs;
#override
void onInit() {
ever(count1, (_) => print("$_ has been changed"));
super.onInit();
}
}
But how to use "ever()" or triggering specific callbacks when variable change if the variable doesn't have (.obs) because I am using GetBuilder (not : Obx or GetX) in my view
class AnyScreen extends StatelessWidget {
final controller = Get.put(CounterController());
#override
Widget build(BuildContext context) {
ever(controller.counter, (value) => print("$value has been changed"));
}
#override
Widget build(BuildContext context) {
return Text("whatever!");
}
}
NOTE: Workers should always be used when starting a Controller or Class, so it should always be on onInit (recommended), Class constructor, or the initState of a StatefulWidget (this practice is not recommended in most cases, but it shouldn't have any side effects)
docs