Closed darwin-morocho closed 3 years ago
@darwin-morocho an autoDispose provider considers a provider is no longer being when it is not being used in any widgets or any dependent providers. That's my understanding of it.
@darwin-morocho an autoDispose provider considers a provider is no longer being when it is not being used in any widgets or any dependent providers. That's my understanding of it.
Hi @venkatd you are right but consider the next code
class CounterController extends StateNotifier<int> {
CounterController() : super(0);
void increment() => state++;
}
final counterProvider = StateNotifierProvider.autoDispose(
(ref) {
ref.onDispose(() {
print("onDispose counter");
});
print("new Counter");
return CounterController();
},
);
class Demo extends StatefulWidget {
Demo({Key? key}) : super(key: key);
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
void initState() {
super.initState();
context.read(counterProvider).addListener((state) {
print("Demo State addListener");
});
}
@override
Widget build(BuildContext context) {
return Container();
}
}
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, watch) {
return Scaffold(
appBar: AppBar(
leading: Demo(),
actions: [
IconButton(
icon: Icon(Icons.login),
onPressed: () {
final route = MaterialPageRoute(
builder: (_) => LoginPage(),
);
Navigator.pushReplacement(context, route);
},
),
],
),
body: Center(
child: Text("counter"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(counterProvider).increment();
},
),
);
}
}
The CounterPage
doesn't use the watch method to listen the counterProvider
but the _stateDemo
has a listener to listen the changes in my CounterController
if I use context.read(counterProvider).increment();
to increment the counter the counterPrivoder creates a new instance of CounterController
it is destroyed immediately and the listener in _stateDemo
is never called.
But if I use the watch
method inside the build method
of my CounterPage
the CounterController
is destroyed when the counter page is destroyed.
@darwin-morocho You could replace your addListener with a ProviderListener widget inside the build method of _DemoState.
That's a good observation. I should note that, watch
is what you would be recommended to use. It tells the system "I need this value here". While read is like saying "give me whatever is here right now".
You can't rely on read to make sure something will stay available for you.
autoDispose relies on how many listeners a provider has, not how many listeners the exposed object has
StateNotifier.addListener
has no impact.
@darwin-morocho You could replace your addListener with a ProviderListener widget inside the build method of _DemoState.
class _DemoState extends State<Demo> {
@override
void initState() {
super.initState();
context.read(counterProvider).addListener((state) {
print("Demo State addListener");
});
}
@override
Widget build(BuildContext context) {
return ProviderListener(
onChange: (_, __) {},
provider: counterProvider,
child: Container(),
);
}
}
Using ProviderListener
the ConterController is created and and destroyed immediately
flutter: new Counter
flutter: Demo State addListener
flutter: onDispose counter
@darwin-morocho I think that dispose comes from the read inside the initState, but can't tell for sure. With the ProviderListener you don't need the initState code. You can put the print inside the onChange callback.
@darwin-morocho I would put a print statement inside of initState
and inside of build
. You're probably calling context.read
before build
is ever called which triggers the quick create/destroy.
I would also recommend taking a look at hooks_riverpod which, IMO, simplifies dealing with widget lifecycles and accessing provider values.
@darwin-morocho Your snippet is wrong I tried this myself and the provider is not disposed.
There's most likely something other than what you gave that causes dispose to be called.
@rrousselGit related to this, how long does riverpod wait before disposing an unused provider? Is it a single frame?
Yes, a frame
As such, the read inside initState followed ProviderListener cannot cause the provider to be disposed between the read and the creation of ProviderListener. Because both reads are within the same frame.
Yes, a frame
As such, the read inside initState followed ProviderListener cannot cause the provider to be disposed between the read and the creation of ProviderListener. Because both reads are within the same frame.
Useful to know!
@darwin-morocho Your snippet is wrong I tried this myself and the provider is not disposed.
There's most likely something other than what you gave that causes dispose to be called.
This is the complete code
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterPage(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CounterController extends StateNotifier<int> {
CounterController() : super(0);
void increment() => state++;
}
final counterProvider = StateNotifierProvider.autoDispose(
(ref) {
ref.onDispose(() {
print("onDispose counter");
});
print("new Counter");
return CounterController();
},
);
class Demo extends StatefulWidget {
Demo({Key? key}) : super(key: key);
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
void initState() {
super.initState();
context.read(counterProvider).addListener((state) {
print("Demo State addListener");
});
}
@override
Widget build(BuildContext context) {
return Container();
}
}
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, watch) {
return Scaffold(
appBar: AppBar(
title: Demo(),
),
body: Center(
child: Text("counter"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(counterProvider).increment();
},
),
);
}
}
If Do I not use the watch method or even a ProviderListener my provider should not be destroyed?
If Do I not use the watch method or even a ProviderListener my provider should not be destroyed?
That's the opposite.
Because you are not using watch/ProviderListener, then your provider is destroyed.
Use ProviderListener instead of initState + read(provider).addListener
If Do I not use the watch method or even a ProviderListener my provider should not be destroyed?
That's the opposite.
Because you are not using watch/ProviderListener, then your provider is destroyed.
Use ProviderListener instead of initState + read(provider).addListener
Thanks Now I understand How the auto dispose works
Hi @rrousselGit thanks for this proyecto. Could you help me to understand something?
I have a project with a personal state managment based of Provider now I am changing the logic to be similar to riverpod but I was not able to replicate the autoDispose feature.
How a provider can know that it no longer needs it if the Provider is not linked to a statefulwidget?
Thanks for your response