import 'dart:async'; import 'package:async_tools/async_tools.dart'; import 'package:bloc/bloc.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; typedef BlocMapState = IMap; class _ItemEntry { _ItemEntry({required this.bloc, required this.subscription}); final B bloc; final StreamSubscription subscription; } abstract class BlocMapCubit> extends Cubit> { BlocMapCubit() : _entries = {}, _tagLock = AsyncTagLock(), super(IMap()); @override Future close() async { await _entries.values.map((e) => e.subscription.cancel()).wait; await _entries.values.map((e) => e.bloc.close()).wait; await super.close(); } Future add(MapEntry Function() create) { // Create new element final newElement = create(); final key = newElement.key; final bloc = newElement.value; return _tagLock.protect(key, closure: () async { // Remove entry with the same key if it exists await _internalRemove(key); // Add entry with this key _entries[key] = _ItemEntry( bloc: bloc, subscription: bloc.stream.listen((data) { // Add sub-cubit's state to the map state emit(state.add(key, data)); })); emit(state.add(key, bloc.state)); }); } Future addState(K key, S value) => _tagLock.protect(key, closure: () async { // Remove entry with the same key if it exists await _internalRemove(key); emit(state.add(key, value)); }); Future _internalRemove(K key) async { final sub = _entries.remove(key); if (sub != null) { await sub.subscription.cancel(); await sub.bloc.close(); } } Future remove(K key) => _tagLock.protect(key, closure: () async { await _internalRemove(key); emit(state.remove(key)); }); R operate(K key, {required R Function(B bloc) closure}) { final bloc = _entries[key]!.bloc; return closure(bloc); } R? tryOperate(K key, {required R Function(B bloc) closure}) { final entry = _entries[key]; if (entry == null) { return null; } return closure(entry.bloc); } Future operateAsync(K key, {required Future Function(B bloc) closure}) => _tagLock.protect(key, closure: () async { final bloc = _entries[key]!.bloc; return closure(bloc); }); Future tryOperateAsync(K key, {required Future Function(B bloc) closure}) => _tagLock.protect(key, closure: () async { final entry = _entries[key]; if (entry == null) { return null; } return closure(entry.bloc); }); final Map> _entries; final AsyncTagLock _tagLock; }