I have two views HomeView and JokeView.
On the HomeView
I have a button that opens JokeView:
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const JokeView(), maintainState: false),
);
},
child: Text('Joke view'))
On JokeView
I have a BackButton
that closes the view
AppBar(
leading: BackButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const HomeView(), maintainState: false));
},
On JokeView
I also has the ListenableBuilder
that listens to JokeViewModel
which is a ChangeNotifier
ListenableBuilder(
listenable: jokeViewModel,
The JokeViewModel
has the dispose()
method:
class JokeViewModel with ChangeNotifier {
...
@override
void dispose() {
print("dispose is called");
super.dispose();
}
}
I expect this method to be called when JokeView
is removed from the stack, but it never happens. I tried the default backButton, Navigator.pop
, Navigator.pushReplacement
.
Am I doing something wrong? Is it supposed to work? I mean should the framework call this method when the view is popping from the stack or not?
The question is educational and I am aware of StatefulWidgets. I don't use Provider.
I have two views HomeView and JokeView.
On the HomeView
I have a button that opens JokeView:
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const JokeView(), maintainState: false),
);
},
child: Text('Joke view'))
On JokeView
I have a BackButton
that closes the view
AppBar(
leading: BackButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const HomeView(), maintainState: false));
},
On JokeView
I also has the ListenableBuilder
that listens to JokeViewModel
which is a ChangeNotifier
ListenableBuilder(
listenable: jokeViewModel,
The JokeViewModel
has the dispose()
method:
class JokeViewModel with ChangeNotifier {
...
@override
void dispose() {
print("dispose is called");
super.dispose();
}
}
I expect this method to be called when JokeView
is removed from the stack, but it never happens. I tried the default backButton, Navigator.pop
, Navigator.pushReplacement
.
Am I doing something wrong? Is it supposed to work? I mean should the framework call this method when the view is popping from the stack or not?
The question is educational and I am aware of StatefulWidgets. I don't use Provider.
Objects that mix in ChangeNotifier
are a common place where developers work with state and resources that will of course use memory. If these objects are not managed properly during the application's lifecycle, it will lead to memory leaks.
Therefore ChangeNotifier
is disposable and internally in Flutter
during debug
it uses FlutterMemoryAllocations.instance.dispatchObjectCreated
and FlutterMemoryAllocations.instance.dispatchObjectDisposed
to help us, developers, identify potential memory leaks.
However, ChangeNotifier.dispose()
method is not always called by the framework. It depends on how the object was created.
If you create it like:
final jokeViewModel = JokeViewModel();
it becomes your responsibility to call the dispose
method. Maybe you want this object to be long lived and keep it somewhere as a singleton or maybe you want it to have the same lifecycle scope as the host StatefulWidget
, in which case you call dispose
when the host widget is disposed.
If you use Provider
, you can also provide an instance of your ChangeNotifier
in a widget tree. There are two ways to do this:
ChangeNotifierProvider
:ChangeNotifierProvider(
create: (context) => JokeViewModel(...),
child: ...
)
In this case, it becomes the responsibility of the ChangeNotifierProvider
to call dispose
on the instance of ChangeNotifier
that it creates. This happens when the ChangeNotifierProvider
itself is disposed.
ChangeNotifier
:ChangeNotifierProvider.value(
value: jokeViewModelCreatedElsewhere,
child: ...
)
In this case, the ChangeNotifierProvider
can not and will not be responsible for calling dispose
on the jokeViewModelCreatedElsewhere
because it does not own it. Disposing it could lead to errors if the intention of the caller was to share this instance with multiple widgets (eg. you have a ChangeNotifier
that is used in multiple modals, it doesn't make sense for the first modal to dispose it when it's closed).