refactor bloc tools to its own package

This commit is contained in:
Christien Rioux 2024-02-26 23:34:17 -05:00
parent 8e7619677a
commit 43b01c7555
34 changed files with 284 additions and 146 deletions

View file

@ -1,42 +0,0 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:bloc/bloc.dart';
class AsyncTransformerCubit<T, S> extends Cubit<AsyncValue<T>> {
AsyncTransformerCubit(this.input, {required this.transform})
: super(const AsyncValue.loading()) {
_asyncTransform(input.state);
_subscription = input.stream.listen(_asyncTransform);
}
void _asyncTransform(AsyncValue<S> newInputState) {
_singleStateProcessor.updateState(newInputState, closure: (newState) async {
// Emit the transformed state
try {
if (newState is AsyncLoading<S>) {
emit(const AsyncValue.loading());
} else if (newState is AsyncError<S>) {
emit(AsyncValue.error(newState.error, newState.stackTrace));
} else {
final transformedState = await transform(newState.data!.value);
emit(transformedState);
}
} on Exception catch (e, st) {
emit(AsyncValue.error(e, st));
}
});
}
@override
Future<void> close() async {
await _subscription.cancel();
await input.close();
await super.close();
}
Cubit<AsyncValue<S>> input;
final SingleStateProcessor<AsyncValue<S>> _singleStateProcessor =
SingleStateProcessor();
Future<AsyncValue<T>> Function(S) transform;
late final StreamSubscription<AsyncValue<S>> _subscription;
}

View file

@ -1,112 +0,0 @@
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<K, S> = IMap<K, S>;
class _ItemEntry<S, B> {
_ItemEntry({required this.bloc, required this.subscription});
final B bloc;
final StreamSubscription<S> subscription;
}
// Streaming container cubit that is a map from some immutable key
// to a some other cubit's output state. Output state for this container
// cubit is an immutable map of the key to the output state of the contained
// cubits.
//
// K = Key type for the bloc map, used to look up some mapped cubit
// S = State type for the value, keys will look up values of this type
// B = Bloc/cubit type for the value, output states of type S
abstract class BlocMapCubit<K, S, B extends BlocBase<S>>
extends Cubit<BlocMapState<K, S>> {
BlocMapCubit()
: _entries = {},
_tagLock = AsyncTagLock(),
super(IMap<K, S>());
@override
Future<void> close() async {
await _entries.values.map((e) => e.subscription.cancel()).wait;
await _entries.values.map((e) => e.bloc.close()).wait;
await super.close();
}
Future<void> add(MapEntry<K, B> 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<void> 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<void> _internalRemove(K key) async {
final sub = _entries.remove(key);
if (sub != null) {
await sub.subscription.cancel();
await sub.bloc.close();
}
}
Future<void> remove(K key) => _tagLock.protect(key, closure: () async {
await _internalRemove(key);
emit(state.remove(key));
});
R operate<R>(K key, {required R Function(B bloc) closure}) {
final bloc = _entries[key]!.bloc;
return closure(bloc);
}
R? tryOperate<R>(K key, {required R Function(B bloc) closure}) {
final entry = _entries[key];
if (entry == null) {
return null;
}
return closure(entry.bloc);
}
Future<R> operateAsync<R>(K key,
{required Future<R> Function(B bloc) closure}) =>
_tagLock.protect(key, closure: () async {
final bloc = _entries[key]!.bloc;
return closure(bloc);
});
Future<R?> tryOperateAsync<R>(K key,
{required Future<R> Function(B bloc) closure}) =>
_tagLock.protect(key, closure: () async {
final entry = _entries[key];
if (entry == null) {
return null;
}
return closure(entry.bloc);
});
final Map<K, _ItemEntry<S, B>> _entries;
final AsyncTagLock<K> _tagLock;
}

View file

@ -1,12 +0,0 @@
import 'package:bloc/bloc.dart';
mixin BlocTools<State> on BlocBase<State> {
void withStateListen(void Function(State event)? onData,
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
if (onData != null) {
onData(state);
}
stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
}

View file

@ -1,15 +0,0 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:bloc/bloc.dart';
abstract class FutureCubit<State> extends Cubit<AsyncValue<State>> {
FutureCubit(Future<State> fut) : super(const AsyncValue.loading()) {
unawaited(fut.then((value) {
emit(AsyncValue.data(value));
// ignore: avoid_types_on_closure_parameters
}, onError: (Object e, StackTrace stackTrace) {
emit(AsyncValue.error(e, stackTrace));
}));
}
}

View file

@ -1,58 +0,0 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
// Mixin that automatically keeps two blocs/cubits in sync with each other
// Useful for having a BlocMapCubit 'follow' the state of another input cubit.
// As the state of the input cubit changes, the BlocMapCubit can add/remove
// mapped Cubits that automatically process the input state reactively.
//
// S = Input state type
// K = Key derived from elements of input state
// V = Value derived from elements of input state
abstract mixin class StateFollower<S extends Object, K, V> {
void follow({
required S initialInputState,
required Stream<S> stream,
}) {
//
_lastInputStateMap = getStateMap(initialInputState);
_subscription = stream.listen(_updateFollow);
}
Future<void> close() async {
await _subscription.cancel();
}
IMap<K, V> getStateMap(S state);
Future<void> removeFromState(K key);
Future<void> updateState(K key, V value);
void _updateFollow(S newInputState) {
_singleStateProcessor.updateState(getStateMap(newInputState),
closure: (newStateMap) async {
for (final k in _lastInputStateMap.keys) {
if (!newStateMap.containsKey(k)) {
// deleted
await removeFromState(k);
}
}
for (final newEntry in newStateMap.entries) {
final v = _lastInputStateMap.get(newEntry.key);
if (v == null || v != newEntry.value) {
// added or changed
await updateState(newEntry.key, newEntry.value);
}
}
// Keep this state map for the next time
_lastInputStateMap = newStateMap;
});
}
late IMap<K, V> _lastInputStateMap;
final SingleStateProcessor<IMap<K, V>> _singleStateProcessor =
SingleStateProcessor();
late final StreamSubscription<S> _subscription;
}

View file

@ -1,25 +0,0 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:bloc/bloc.dart';
abstract class StreamWrapperCubit<State> extends Cubit<AsyncValue<State>> {
StreamWrapperCubit(Stream<State> stream, {State? defaultState})
: super(defaultState != null
? AsyncValue.data(defaultState)
: const AsyncValue.loading()) {
_subscription = stream.listen((event) => emit(AsyncValue.data(event)),
// ignore: avoid_types_on_closure_parameters
onError: (Object error, StackTrace stackTrace) {
emit(AsyncValue.error(error, stackTrace));
});
}
@override
Future<void> close() async {
await _subscription.cancel();
await super.close();
}
late final StreamSubscription<State> _subscription;
}

View file

@ -1,19 +1,12 @@
export 'animations.dart';
export 'async_transformer_cubit.dart';
export 'bloc_map_cubit.dart';
export 'bloc_tools.dart';
export 'enter_password.dart';
export 'enter_pin.dart';
export 'future_cubit.dart';
export 'loggy.dart';
export 'phono_byte.dart';
export 'responsive.dart';
export 'scanner_error_widget.dart';
export 'shared_preferences.dart';
export 'state_follower.dart';
export 'state_logger.dart';
export 'stream_listenable.dart';
export 'stream_wrapper_cubit.dart';
export 'transformer_cubit.dart';
export 'widget_helpers.dart';
export 'window_control.dart';

View file

@ -1,21 +0,0 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
class TransformerCubit<T, S> extends Cubit<T> {
TransformerCubit(this.input, {required this.transform})
: super(transform(input.state)) {
_subscription = input.stream.listen((event) => emit(transform(event)));
}
@override
Future<void> close() async {
await _subscription.cancel();
await input.close();
await super.close();
}
Cubit<S> input;
T Function(S) transform;
late final StreamSubscription<S> _subscription;
}