-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add emitForEach / emitOnEach Methods (or something similar) to Cubit #3555
Comments
I think the alternative solution you proposed of exposing an It would be even cooler if the |
This would be a great feature. Are there any plans of implementing this? Or any ways to contribute to this? |
+1 |
It would be great if Bloc supported this. Until then, the code below might be useful; I just made some changes to the Emitter class. base class StreamableCubit<State> extends Cubit<State> {
StreamableCubit(super.initialState);
final _completer = Completer<void>();
final _disposables = <FutureOr<void> Function()>[];
Future<void> get future => _completer.future;
@override
Future<void> close() async {
for (final disposable in _disposables) {
disposable.call();
}
_disposables.clear();
if (!_completer.isCompleted) _completer.complete();
super.close();
}
Future<void> onEach<T>(
Stream<T> stream, {
required void Function(T data) onData,
void Function(Object error, StackTrace stackTrace)? onError,
}) {
final completer = Completer<void>();
final subscription = stream.listen(
onData,
onDone: completer.complete,
onError: onError ?? completer.completeError,
cancelOnError: onError == null,
);
_disposables.add(subscription.cancel);
return Future.any([future, completer.future]).whenComplete(() {
subscription.cancel();
_disposables.remove(subscription.cancel);
});
}
Future<void> forEach<T>(
Stream<T> stream, {
required State Function(T data) onData,
State Function(Object error, StackTrace stackTrace)? onError,
}) {
return onEach<T>(
stream,
onData: (data) => emit(onData(data)),
onError: onError != null
? (Object error, StackTrace stackTrace) {
emit(onError(error, stackTrace));
}
: null,
);
}
} |
I've thought about this a bit and have come to the conclusion that the bloc/emitter APIs don't really make sense in the context of cubit because the API doesn't provide a way for developers to control the lifetime of the subscription. For example, if multiple public methods on the cubit used I think the best we could do is offer an API that just registers a subscription for the lifetime of the cubit and the cubit could then handle canceling all subscriptions when it is closed but I'm not sure that is what folks would want: class MyCubit extends Cubit<MyState> {
void doSomething() {
// This would be equivalent to managing the subscription
// and canceling the subscription when the cubit is closed
emit.onEach(stream, ...);
}
void doSomethingElse() {
// This would register another subscription and calling `doSomething` and `doSomethingElse`
// would potentially result in two subscriptions that are active simultaneously.
emit.onEach(stream, ...);
}
} I'm guessing that's not what most people would want and instead they'd want the first subscription to be canceled when a new public method is called. At that point it doesn't seem worth it to introduce new APIs when the |
I've spent a bit more time playing around with this and I actually do think we can support this -- I have a proof of concept and will try to get a PR up shortly 🎉 |
That would be amazing to have |
If people want full control over their methods, they should use Bloc, which is specifically designed for event management. Cubit is better suited for simpler use cases. We don’t always need event management, but sometimes, even in Cubit, we may need to listen to multiple streams in the constructor and dispose of them when the Cubit is closed. |
I have a draft PR up at #4293. The PR refactors cubit to share the I'm still not convinced this is worth adding to |
We have 30+ Flutter apps and millions lines of code and we never really go for Bloc, since Cubits are way more convenient to use. There are only a few use cases where Blocs are the right choice. Therefore, I am really looking forward for the feature to be released |
Thanks for the PR! However, this implementation isn't exactly what I'm looking for. I believe it may confuse users, as it could lead to unexpected behavior without clear explanations. As I mentioned, if developers need to listen to a stream upon a user tap, they should either use Bloc, which is designed for handling events, or handle it manually. Additionally, there's a way to add a tag in the emit.forEach or emit.onEach method in Cubits. This allows you to detect whether the provided stream is already being listened or not and implement the logic accordingly. Using this approach, we can achieve both behaviors similar to Bloc. However, the implementation in codes might be slightly more complex. That said, as I mentioned, I'm not a big fan of this approach because if we need event management, we should use the right tool for it, which is Bloc. However, in very common cases where we simply want to listen to one or multiple streams in the constructor of a Cubit and cancel the subscriptions in the close method, it’s better and simpler to use emit.onEach or emit.forEach. These handle cancellation for us, making the code cleaner and more consistent across the project, similar to how it is in Blocs. |
After thinking about this some more and chatting with more folks I don't feel this change provides a ton of value since the functionality in this implementation cancels any pending streams whenever emit.forEach/emit.onEach is used (regardless of the method it's used in). This differs from the bloc implementation since emit.forEach/emit.onEach is scoped to the event handler so emit.forEach in event handler A won't cancel the subscription for emit.forEach in event handler B. This isn't possible to do with Cubit (at least not with the current public API) and I don't think this change is worth it since I fear it won't solve real problems folks have and it'll lead to confusion since the API is named the same but functions differently across bloc and cubit. |
Description
When working with a
Cubit
that consume some otherStream
you need to maintain aStreamSubscription
during the life of theCubit
and close it etc. This comes with some extra work which is handled well inBloc
viaemit.forEach
/emit.onEach
but no similar solution exists for that when usingCubit
.Desired Solution
Adding the methods
emitForEach
andemitOnEach
with same signatures and functionality as their counter part inEmitter
.Alternatives Considered
Maybe give a
Cubit
its ownEmitter
.The text was updated successfully, but these errors were encountered: