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, 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( listenWhen: (previous, current) => selector(previous) != selector(current), listener: (context, state) => listener(context, selector(state)), child: child, ); } final BlocWidgetSelector 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 on Emitter { Future forEachIgnoreError( Stream stream, { required State Function(T data) onData, }) => onEach( stream, onData: (data) => call(onData(data)), onError: (_, __) {}, ); } extension BlocExtension on Bloc { void safeAdd(E event) { if (!isClosed) { add(event); } } } class _BlocForEachObj { const _BlocForEachObj(this.subscription, this.completer); final StreamSubscription subscription; final Completer completer; } mixin BlocForEachMixin implements Bloc { @override Future 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 forEach( Emitter emit, Stream 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>[]; }