2024-06-17 18:04:53 +02:00
|
|
|
import 'dart:async';
|
|
|
|
|
2024-01-12 19:53:14 +01:00
|
|
|
import 'package:flutter/material.dart';
|
2023-08-19 18:47:56 +02:00
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
2024-01-12 19:53:14 +01:00
|
|
|
import 'package:provider/single_child_widget.dart';
|
2023-08-19 18:47:56 +02:00
|
|
|
|
2023-07-28 18:48:50 +02:00
|
|
|
mixin BlocLogger {
|
2023-06-18 16:26:19 +02:00
|
|
|
String? get tag => null;
|
|
|
|
|
|
|
|
bool Function(dynamic currentState, dynamic nextState)? get shouldLog => null;
|
2023-04-13 17:32:31 +02:00
|
|
|
}
|
2023-05-29 18:55:10 +02:00
|
|
|
|
2024-01-12 19:53:14 +01:00
|
|
|
class BlocListenerT<B extends StateStreamable<S>, S, T>
|
|
|
|
extends SingleChildStatelessWidget {
|
|
|
|
const BlocListenerT({
|
|
|
|
super.key,
|
2024-05-25 09:51:42 +02:00
|
|
|
super.child,
|
2024-01-12 19:53:14 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-29 18:55:10 +02:00
|
|
|
/// 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;
|
|
|
|
}
|
2023-08-19 18:47:56 +02:00
|
|
|
|
|
|
|
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: (_, __) {},
|
|
|
|
);
|
|
|
|
}
|
2024-01-12 19:53:14 +01:00
|
|
|
|
|
|
|
extension BlocExtension<E, S> on Bloc<E, S> {
|
|
|
|
void safeAdd(E event) {
|
|
|
|
if (!isClosed) {
|
|
|
|
add(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-17 18:04:53 +02:00
|
|
|
|
|
|
|
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>[];
|
|
|
|
}
|