How to use dispose with flutter bloc? - flutter

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

Related

Flutter Change Notifier dispose individual listener

I'm currently adding a listener to a screen like this:
#override
void initState() {
person.addListener(() {
doSomething();
});
super.initState();
}
This works, but I have difficulties with correctly disposing the listener after I will call Navigator pop.
I tried calling dispose
#override
void dispose() {
person.dispose();
super.dispose();
}
But this disposes all the listeners on "person". Which are active on other parts in the app.
Is there a possibility to assign a key to the addListener and dispose only that addListener?
Thanks.
You could say person.removeListener(yourListener) in your dispose method

Get a provider method in the dispose method not working

I'm trying to access a provider method in the dispose function.
#override
void dispose() {
if (canRemoveData) Provider.of<MyProvider>(context, listen: false).clearData();
super.dispose();
}
but when that gets called I get the error:
The following assertion was thrown while finalizing the widget tree:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer
stable.
What am I doing wrong and how can I fix it?
The error description basically says it all. The dispose() method is intended for disposing your widget's state dependencies and controllers, so you shouldn't try to find ancestors of this widget at this point because your widget is already deleted from the widget tree.
You should instead make a variable in your state and provide MyProvider value in initState(). Then you would be able to call MyProvider#clearData() from dispose as such:
class _MyWidgetState extends State<MyWidget> {
MyProvider _myProvider;
#override
void initState() {
super.initState();
_myProvider = Provider.of<MyProvider>(context, listen: false);
}
#override
void dispose() {
if (canRemoveData) _myProvider.clearData();
super.dispose();
}
#override
Widget build(BuildContext context) {
// implement
}
}
Also if you provide MyProvider as a direct parent of your widget and after it's dispose you wouldn't need MyProvider anywhere else, you could use ProxyProvider dispose method like that:
ProxyProvider<SomeDependency, MyProvider>(
update: (context, someDependency, previous) => previous ?? MyProvider(someDependency),
dispose: (context, myProvider) => myProvider.clearData(),
),
you can override deactivate method in StatefullWidget
#override
void deactivate() {
super.deactivate();
context.read<PinScreenProvider>().destroy();
}
Note: destroy is a function, where i written clear data

Does the WidgetsBindingObserver work on a stateless widget?

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

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.

Get InheritedWidget parameter in initState

i need some help understanding how to obtain data from inherited widget.
I usually get the parameter from my widget directly from the build method using
#override
Widget build(BuildContext context) {
//THIS METHOD
var data = StateContainer.of(context).data;
return Container(child:Text("${data.parameter}"));
}
But this method cant be called from initState since there is no buildContext yet.
I need in the initState method to have that parameter (i call my fetch from server in that and i need to pass that data to my function), so, how should i do it?
#override
void initState() {
otherData = fetchData(data);
super.initState();
}
I tried using didChangeDipendencies() but it is called every time the view is rebuilt (popping from screen, etc.) so it is not what i want to use and neither the FutureBuilder widget.
Any suggestion?
First, note that you probably do want to use didChangeDependencies. But you can't just do your call there without any check. You need to wrap it in an if first.
A typical didChangeDependencies implementation should look similar to:
Foo foo;
#override
void didChangeDependencies() {
super.didChangeDependencies();
final foo = Foo.of(context);
if (this.foo != foo) {
this.foo = foo;
foo.doSomething();
}
}
Using such code, doSomething will be executed only when foo changes.
Alternatively, if you are lazy and know for sure that your object will never ever change, there's another solution.
To obtain an InheritedWidget, the method typically used is:
BuildContext context;
InheritedWidget foo = context.inheritFromWidgetOfExactType(Foo);
and it is this method that cannot be called inside initState.
But there's another method that does the same thing:
BuildContext context;
InheritedWidget foo = context.ancestorInheritedElementForWidgetOfExactType(Foo)?.widget;
The twist is:
- this method can be called inside initState
- it won't handle the scenario where the value changed.
So if your value never changes, you can use that instead.
1, If you only need InheritedWidget as a Provider of parameter for Widget.
You can using on initState as bellow:
#override
void initState() {
super.initState();
var data = context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
}
2, If you need listener to re-render widget when data of InheritedWidget change. I suggest you wrapper your StatefulWidget insider a StatelessWidget,
parameter of StatefulWidget is passed from StatelessWidget, when InheritedWidget change data, it will notify to StatelessWidget, on StatefulWidget we will get change on didChangeDependencies and you can refresh data.
This is code guide:
class WrapperDemoWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
DemoData data = StateContainer.of(context).data;
return Container();
}
}
class ImplementWidget extends StatefulWidget {
DemoData data;
ImplementWidget({this.data});
#override
_ImplementWidgetState createState() => _ImplementWidgetState();
}
class _ImplementWidgetState extends State<ImplementWidget> {
#override
void initState() {
super.initState();
//TODO Do sth with widget.data
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
//TODO Do change with widget.data
}
#override
Widget build(BuildContext context) {
return Container();
}
}
I prefer the solution with didChangeDependencies because Future.delayed solution is a bit hack, looks unprofessional and unhealthy. However, it works out of the box.
This is the solution I prefer:
class _MyAppState extends State<MyApp> {
bool isDataLoaded = false;
#override
void didChangeDependencies() {
if (!isDataLoaded) {
otherData = fetchData(data).then((_){
this.isDataLoaded = true;
});
}
super.didChangeDependencies();
}
...
You can also get the context in initState, try using a future with duration zero. You can find some examples here
void initState() {
super.initState();
Future.delayed(Duration.zero,() {
//use context here
showDialog(context: context, builder: (context) => AlertDialog(
content: Column(
children: <Widget>[
Text('#todo')
],
),
actions: <Widget>[
FlatButton(onPressed: (){
Navigator.pop(context);
}, child: Text('OK')),
],
));
});
}
i use it to make loading screens using inherited widgets and avoid some global variables