nc-photos/app/lib/bloc_util.dart
2024-06-18 01:36:45 +08:00

101 lines
2.5 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/single_child_widget.dart';
mixin BlocLogger {
String? get tag => null;
bool Function(dynamic currentState, dynamic nextState)? get shouldLog => null;
}
class BlocListenerT<B extends StateStreamable<S>, S, T>
extends SingleChildStatelessWidget {
const BlocListenerT({
super.key,
super.child,
required this.selector,
required this.listener,
});
@override
Widget buildWithChild(BuildContext context, Widget? child) {
return BlocListener<B, S>(
listenWhen: (previous, current) =>
selector(previous) != selector(current),
listener: (context, state) => listener(context, selector(state)),
child: child,
);
}
final BlocWidgetSelector<S, T> selector;
final void Function(BuildContext context, T state) listener;
}
/// Wrap around a string such that two strings with the same value will fail
/// the identical check
class StateMessage {
StateMessage(this.value);
final String value;
}
extension EmitterExtension<State> on Emitter<State> {
Future<void> forEachIgnoreError<T>(
Stream<T> stream, {
required State Function(T data) onData,
}) =>
onEach<T>(
stream,
onData: (data) => call(onData(data)),
onError: (_, __) {},
);
}
extension BlocExtension<E, S> on Bloc<E, S> {
void safeAdd(E event) {
if (!isClosed) {
add(event);
}
}
}
class _BlocForEachObj {
const _BlocForEachObj(this.subscription, this.completer);
final StreamSubscription subscription;
final Completer completer;
}
mixin BlocForEachMixin<E, S> implements Bloc<E, S> {
@override
Future<void> close() async {
for (final e in _forEaches) {
unawaited(e.subscription.cancel());
e.completer.complete();
}
}
// The original emit.forEach is causing the internal eventController in Bloc
// to deadlock when closing, use this instead
Future<void> forEach<T>(
Emitter<S> emit,
Stream<T> stream, {
required S Function(T data) onData,
S Function(Object error, StackTrace stackTrace)? onError,
}) async {
final completer = Completer();
final subscription = stream.listen((event) {
emit(onData(event));
}, onError: (e, stackTrace) {
if (onError != null) {
emit(onError(e, stackTrace));
}
});
_forEaches.add(_BlocForEachObj(subscription, completer));
return completer.future;
}
final _forEaches = <_BlocForEachObj>[];
}