Line data Source code
1 : import '../notifiers/state_notifier.dart';
2 : import 'state_notifier_provider.dart';
3 :
4 : typedef SelectCallback<S, R> = R Function(S);
5 : typedef BuildWhenCallback<S> = bool Function(S prev, S current);
6 :
7 : sealed class FilteredProvider<N, S> {
8 2 : FilteredProvider({required this.notifier});
9 :
10 : final N notifier;
11 :
12 : /// function to rebuild the Consumer
13 : void Function(N notifier)? reaction;
14 :
15 : late void Function(S) listener;
16 : void createListener();
17 : }
18 :
19 : class SelectFilteredProvider<N extends StateNotifier<S>, S, R>
20 : extends FilteredProvider<N, S> implements BaseStateNotifierProvider<N, S> {
21 2 : SelectFilteredProvider({
22 : required super.notifier,
23 : required this.callback,
24 : required this.listenWhenTheCallbackReturnsTrue,
25 : });
26 :
27 : /// callback defined when a filter is used
28 : final SelectCallback<S, R> callback;
29 :
30 : /// if the filter is a selected and we only want to
31 : /// rebuild the consumer when the callback returns true
32 : final bool listenWhenTheCallbackReturnsTrue;
33 :
34 : /// used to store the value returned by .select or .when
35 : late R selectValue;
36 :
37 2 : @override
38 2 : void createListener() => createSelectListener(this);
39 : }
40 :
41 : class WhenFilteredProvider<N extends StateNotifier<S>, S>
42 : extends FilteredProvider<N, S> implements BaseStateNotifierProvider<N, S> {
43 2 : WhenFilteredProvider({
44 : required super.notifier,
45 : required this.callback,
46 : });
47 :
48 : /// callback defined when a filter is used
49 : final BuildWhenCallback<S> callback;
50 :
51 2 : @override
52 2 : void createListener() => createWhenListener(this);
53 : }
54 :
55 : extension StateNotifierProviderExtension<N extends StateNotifier<S>, S, A>
56 : on ListeneableProvider<N, S, A> {
57 2 : SelectFilteredProvider<N, S, R> select<R>(
58 : SelectCallback<S, R> callback, {
59 : String? tag,
60 : bool booleanCallback = false,
61 : }) {
62 2 : return SelectFilteredProvider(
63 : callback: callback,
64 2 : notifier: read(tag: tag),
65 : listenWhenTheCallbackReturnsTrue: booleanCallback,
66 : );
67 : }
68 :
69 2 : WhenFilteredProvider<N, S> when<R>(
70 : BuildWhenCallback<S> callback, {
71 : String? tag,
72 : bool booleanCallback = false,
73 : }) {
74 2 : return WhenFilteredProvider(
75 : callback: callback,
76 2 : notifier: read(tag: tag),
77 : );
78 : }
79 : }
80 :
81 : /// create the listener for provider.select filter (StateProvider)
82 2 : void createSelectListener<N extends StateNotifier<S>, S, R>(
83 : SelectFilteredProvider<N, S, R> filter,
84 : ) {
85 8 : R prevValue = filter.callback(filter.notifier.state);
86 2 : filter.selectValue = prevValue;
87 :
88 4 : filter.listener = (newState) {
89 8 : final value = filter.callback(filter.notifier.state);
90 2 : if (filter.listenWhenTheCallbackReturnsTrue) {
91 : assert(
92 2 : value is bool,
93 : 'The value returned by the callback must be a boolean because '
94 : 'listenWhenTheCallbackReturnsTrue is true',
95 : );
96 : }
97 :
98 2 : filter.selectValue = value;
99 :
100 : bool allowRebuild = false;
101 3 : if (value is bool && filter.listenWhenTheCallbackReturnsTrue) {
102 : allowRebuild = value;
103 1 : } else if (prevValue != value) {
104 : allowRebuild = true;
105 : }
106 :
107 : // check if the value has changed
108 2 : if (allowRebuild && filter.reaction != null) {
109 6 : filter.reaction!(filter.notifier);
110 : }
111 : prevValue = value;
112 : };
113 : }
114 :
115 : /// create the listener for provider.when filter
116 2 : void createWhenListener<N extends StateNotifier<S>, S>(
117 : WhenFilteredProvider<N, S> filter,
118 : ) {
119 2 : final notifier = filter.notifier;
120 4 : filter.listener = (newState) {
121 : // rebuild the Consumer using the boolean returned by the callback
122 6 : final allowRebuild = filter.callback(notifier.oldState, newState);
123 : if (allowRebuild) {
124 4 : filter.reaction?.call(notifier);
125 : }
126 : };
127 : }
|