Closed BreX900 closed 2 years ago
Can you provide a gist to reproduce the issue?
On the other hand, If a widget is removed from the widget tree, you must cancel the stream suscription manually in the dispose
method.
If you want to subscribe to the state, you can use ref.listen
final counterProvider = BlocProvider.autodispose<CounterBloc, int>((ref) => CounterBloc(0));
ref.listen(
counterProvider,
(int? previous, int current) {},
);
This must call the cancel when the widget is removed from the widget tree.
But, if you use BlocProvider
instead of AutoDisposeBlocProvider
, the bloc always be alive, and then, the stream will always alive.
The only way to kill the a bloc and its stream in a BlocProvider is to remover the ProviderScope
or using ref.refresh(counterProvider)
.
If you want to learn how to apply the first idea, maybe you want to study this [repository](https://github.com/kranfix/river_navigator) where I explored how to work with nested navigators with multiple ProviderScope
.
It is not possible to cancel the stream of the bloc in a BlocProvider
because if it dies, it can never be alive again.
@kranfix Here is an example: https://gist.github.com/BreX900/f6c67db6a907829010110b726e88a01f
FixedBlocProvider
would be the correct implementation of BlocProvider
in my opinion
If you tap and close the dialog you will see this log series:
I/flutter (21829): Create: Normal // First Dialog open
I/flutter (21829): Listen: Normal
I/flutter (21829): Create: Fixed // First Dialog open
I/flutter (21829): Listen: Fixed
I/flutter (21829): Cancel: Fixed // Dialog closed
I/flutter (21829): Listen: Fixed // Dialog reopen
I/flutter (21829): Cancel: Fixed // Dialog recessed
As you can see, you could stream the bloc with auto dispose active so as to stop listening to the stream if it is no longer necessary. This implementation keeps the block alive, so it will restart listening to the block stream if you reopen the dialog, so if you listen again. Instead the current implementation continues to listen to the block stream even if it is not needed
Simply put, the stream provider should always be an autoDispose and the bloc provider should be the one defined by the developer
I was analyzing your example and I have a question. Instead of using BlocProvider
, why don't you use AutoDisposeBlocProvider
or its equivalente BlocProvider.autodispose
?
The AutoDisposeBlocProvider
must work as your FixedBlocProvider
.
When your dialog is removed from the widget-tree, the bloc.stream
is not disposed only the current subscription.
@kranfix What you get from my example is:
bloc
remains aliveWhat you would get with AutoDisposeBlocProvider
is:
bloc
dies as wellI would like only the subscription to die and not the bloc
I saw that in riverpod in dev version this was added:
2.0.0-dev.2: Added ref.onAddListener, ref.onRemoveListener, ref.onCancel and ref.onResume. All of which allow performing side-effects when providers are listened or stop being listened.
The effect you get with my example is the same as the one you would get with this code in the riverpod version mentioned:
@override
S create(ProviderElementBase<S> ref) {
final bloc = ref.watch(this.bloc);
void listener(S newState) => ref.setState(newState);
int listenersCount = 0;
StreamSubscription? subscription;
ref.onAddListener(() {
listenersCount += 1;
if (listenersCount > 0 && subscription == null) {
subscription = bloc.stream.listen(listener);
}
});
ref.onRemoveListener(() {
listenersCount -= 1;
if (listenersCount < 1 && subscription != null) {
subscription?.cancel();
subscription = null;
}
});
return bloc.state;
}
Haven't tried but the right behavior I would expect would be this :)
The AutoDisposeBlocProvider
will close the bloc and its stream when it is not listened/watched anymore.
The idea is to avoid stream subscriptions
Listening to the bloc stream for
BlocProvider
happens forever once listening has started. In my opinion the only resource that should live forever is the bloc. On the other hand, when the widget that is listening to the stream is no longer in the UI, this stream should no longer be listened toYou can verify this problem with: (Bloc/Cubit)
Cancel will never be called as listening to the stream always remains alive
The correct implementation would be to update
BlocProvider
with:I hope I have explained, the behavior does not want to be that of
BlocProvider.autoDispose
but only free them to subscribe to the stream, not the entire bloc