From 44c07dfb0ae71e3d4868b653240aab0d8f090c3c Mon Sep 17 00:00:00 2001 From: Andrii Date: Wed, 24 Jan 2024 14:13:15 +0100 Subject: [PATCH 1/3] added filter action for action_bloc_listener --- CHANGELOG.md | 4 ++ README.md | 11 +++-- lib/src/bloc_action_listener.dart | 13 ++++-- lib/src/mixins/bloc_action_mixin.dart | 16 ++++++++ pubspec.yaml | 2 +- test/bloc_action_listener_test.dart | 58 +++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4964b16..0028b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.5 + +- Allow filter actions in Bloc using actionWhen in useActionListener + ## 1.4.4 - Allow put void type to generic type diff --git a/README.md b/README.md index 154baf7..429c6b7 100644 --- a/README.md +++ b/README.md @@ -391,9 +391,14 @@ Then, consume results as you would do with `useBlocListener` @override Widget build(BuildContext context) { // Handle separate action stream with values other than a state type - useActionListener(cubit, (String action) { - _showMessage(context, action); - }); + useActionListener( + cubit, + (String action) { + _showMessage(context, action); + }, + // If you need, you can filter actions + actionWhen: (previousAction, action) => true, + ); return // Build your widget } diff --git a/lib/src/bloc_action_listener.dart b/lib/src/bloc_action_listener.dart index 57a7dba..ad60e63 100644 --- a/lib/src/bloc_action_listener.dart +++ b/lib/src/bloc_action_listener.dart @@ -3,14 +3,21 @@ import 'package:hooked_bloc/hooked_bloc.dart'; typedef OnActionCallback = Function(T action); +typedef ActionListenerCondition = bool Function(T? previousAction, T action); + /// Calls callback function [onAction] each time, when new action is dispatched from [BlocBase] with [BlocActionMixin], [ActionCubit] or [ActionBloc] void useActionListener( BlocActionMixin actionMixin, - OnActionCallback onAction, -) { + OnActionCallback onAction, { + ActionListenerCondition? actionWhen, +}) { useEffect( () { - final subscription = actionMixin.actions.listen(onAction); + final subscription = actionMixin.actions.listen((action) { + if(actionWhen == null || actionWhen(actionMixin.previousAction, action)) { + onAction(action); + } + }); return subscription.cancel; }, [actionMixin], diff --git a/lib/src/mixins/bloc_action_mixin.dart b/lib/src/mixins/bloc_action_mixin.dart index 6cadb1c..1c56c37 100644 --- a/lib/src/mixins/bloc_action_mixin.dart +++ b/lib/src/mixins/bloc_action_mixin.dart @@ -32,10 +32,24 @@ import 'package:meta/meta.dart'; /// ... /// } /// ``` +/// +/// You can also filter your actions using actionWhen. +/// ```dart +/// useActionListener( +/// cubit, +/// (String action) { +/// //do sth with filtered action +/// _showMessage(context, action); +/// }, +/// actionWhen: (previousAction, action) => true, +/// }); +/// ``` /// See also [ActionCubit] and [ActionBloc] mixin BlocActionMixin on BlocBase { final _streamController = StreamController.broadcast(); + ACTION? previousAction; + Stream get actions => _streamController.stream; @visibleForTesting @@ -44,10 +58,12 @@ mixin BlocActionMixin on BlocBase { @protected void dispatch(ACTION action) { _streamController.add(action); + previousAction = action; } @override Future close() async { + previousAction = null; await Future.wait([ super.close(), _streamController.close(), diff --git a/pubspec.yaml b/pubspec.yaml index 4ad981f..ec38590 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: hooked_bloc description: Flutter package that simplifies injection and usage of Bloc/Cubit. -version: 1.4.4 +version: 1.4.5 repository: https://github.com/Iteo/hooked_bloc issue_tracker: https://github.com/Iteo/hooked_bloc/issues homepage: https://github.com/Iteo/hooked_bloc diff --git a/test/bloc_action_listener_test.dart b/test/bloc_action_listener_test.dart index d848191..8570af7 100644 --- a/test/bloc_action_listener_test.dart +++ b/test/bloc_action_listener_test.dart @@ -16,6 +16,10 @@ class CounterActionCubit extends ActionCubit { void dispatchAction() { dispatch(state.toString()); } + + void dispatchActionValue(String value) { + dispatch(value); + } } void main() { @@ -83,5 +87,59 @@ void main() { expect(listenerCalls, i); } }); + + testWidgets( + 'when Cubit dispatch multiple times, actionWhen should be called only with 2 chars', + (tester) async { + int listenerCalls = 0; + + Widget Function(BuildContext) builder( + ActionCubit cubit, + ) { + return (context) { + useActionListener( + cubit, + (ACTION action) { + listenerCalls++; + }, + actionWhen: (previousAction, action) => action.length == 2 + ); + + return Container(); + }; + } + + CounterActionCubit cubit = CounterActionCubit(); + + HookBuilder hookWidget = HookBuilder(builder: builder(cubit)); + + cubit.dispatchActionValue('1'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 0); + + cubit.dispatchActionValue('11'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 1); + + cubit.dispatchActionValue('2'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 1); + + cubit.dispatchActionValue('22'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 2); + + cubit.dispatchActionValue('333'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 2); + + cubit.dispatchActionValue('4'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 2); + + cubit.dispatchActionValue('55'); + await tester.pumpWidget(hookWidget); + expect(listenerCalls, 3); + }); }); } From 2243fb31e1ae373a52cf4da06f14b4b121eefa9f Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 25 Jan 2024 09:34:34 +0100 Subject: [PATCH 2/3] fix url and format action_bloc.dart file --- README.md | 2 +- lib/src/bloc/action_bloc.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 429c6b7..1516688 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ This code is functionally equivalent to the previous example. It still rebuilds right time. Whole logic of finding adequate Cubit/Bloc and providing current state is hidden in `useBloc` and `useBlocBuilder` hooks. -Full example can be found in here +Full example can be found in here ## Setup diff --git a/lib/src/bloc/action_bloc.dart b/lib/src/bloc/action_bloc.dart index f49f07e..d1fb77a 100644 --- a/lib/src/bloc/action_bloc.dart +++ b/lib/src/bloc/action_bloc.dart @@ -16,7 +16,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; /// } /// } ///``` -abstract class ActionBloc - extends Bloc with BlocActionMixin { +abstract class ActionBloc extends Bloc + with BlocActionMixin { ActionBloc(super.initialState); } From 959e234ad34970a1dc11f9603d74c0462c04f6a4 Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 25 Jan 2024 09:56:17 +0100 Subject: [PATCH 3/3] changed version number --- CHANGELOG.md | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0028b92..a9e1e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.4.5 +## 1.5.0 - Allow filter actions in Bloc using actionWhen in useActionListener diff --git a/pubspec.yaml b/pubspec.yaml index ec38590..b9533c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: hooked_bloc description: Flutter package that simplifies injection and usage of Bloc/Cubit. -version: 1.4.5 +version: 1.5.0 repository: https://github.com/Iteo/hooked_bloc issue_tracker: https://github.com/Iteo/hooked_bloc/issues homepage: https://github.com/Iteo/hooked_bloc