Line data Source code
1 : library notifier_extension; 2 : 3 : import 'dart:async'; 4 : 5 : import 'package:state_notifier/state_notifier.dart'; 6 : 7 : class _FunctionalStateNotifier<S, T> extends StateNotifier<T> { 8 : final StateNotifier<S> _source; 9 : final String name; 10 2 : _FunctionalStateNotifier(this._source, {this.name}) : super(null); 11 : RemoveListener _sourceDisposeFn; 12 : Timer _timer; 13 : 14 1 : StateNotifier<T> where(bool Function(S) test) { 15 4 : _sourceDisposeFn = _source.addListener((_state) { 16 1 : if (test(_state)) { 17 1 : state = _state as T; 18 : } 19 : }, fireImmediately: false); 20 : return this; 21 : } 22 : 23 1 : StateNotifier<void> forEach(void Function(S) action) { 24 3 : _sourceDisposeFn = _source.addListener(action, fireImmediately: false); 25 : return this; 26 : } 27 : 28 1 : StateNotifier<T> map(T Function(S) convert) { 29 4 : _sourceDisposeFn = _source.addListener((state) { 30 2 : super.state = convert(state); 31 : }, fireImmediately: false); 32 : return this; 33 : } 34 : 35 : final _bufferedState = <S>[]; 36 : 37 1 : StateNotifier<T> throttle(Duration duration) { 38 2 : _timer = _makeTimer(duration); 39 4 : _sourceDisposeFn = _source.addListener((model) { 40 2 : _bufferedState.add(model); 41 : }, fireImmediately: false); 42 : return this; 43 : } 44 : 45 1 : Timer _makeTimer(Duration duration) { 46 2 : return Timer(duration, () { 47 1 : if (mounted) { 48 2 : if (_bufferedState.isNotEmpty) { 49 2 : super.state = _bufferedState as T; // since T == List<S>; 50 2 : _bufferedState.clear(); // clear buffer 51 : } 52 2 : _timer = _makeTimer(duration); // reset timer 53 : } 54 : }); 55 : } 56 : 57 1 : @override 58 : RemoveListener addListener( 59 : Listener<T> listener, { 60 : bool fireImmediately = true, 61 : }) { 62 : final dispose = 63 1 : super.addListener(listener, fireImmediately: fireImmediately); 64 1 : return () { 65 1 : dispose.call(); 66 2 : _timer?.cancel(); 67 2 : _sourceDisposeFn?.call(); 68 : }; 69 : } 70 : 71 1 : @override 72 : void dispose() { 73 1 : if (mounted) { 74 1 : super.dispose(); 75 : } 76 2 : _source.dispose(); 77 2 : _timer?.cancel(); 78 : } 79 : } 80 : 81 : /// Functional utilities for [StateNotifier] 82 : extension StateNotifierX<T> on StateNotifier<T> { 83 : /// Filters incoming events by [test] 84 1 : StateNotifier<T> where(bool Function(T) test) { 85 2 : return _FunctionalStateNotifier<T, T>(this, name: 'where').where(test); 86 : } 87 : 88 : /// Maps events of type [T] onto events of type [R] via [convert] 89 1 : StateNotifier<R> map<R>(R Function(T) convert) { 90 2 : return _FunctionalStateNotifier<T, R>(this, name: 'map').map(convert); 91 : } 92 : 93 : /// Applies a function [action] to every incoming event of type [T] 94 1 : StateNotifier<void> forEach(void Function(T) action) { 95 1 : return _FunctionalStateNotifier<T, void>(this, name: 'forEach') 96 1 : .forEach(action); 97 : } 98 : 99 : /// Buffers all incoming [T] events during [duration] and emits 100 : /// them as a [List<T>] (unless there were none) 101 1 : StateNotifier<List<T>> throttle(Duration duration) { 102 1 : return _FunctionalStateNotifier<T, List<T>>(this, name: 'throttle') 103 1 : .throttle(duration); 104 : } 105 : }