Line data Source code
1 : // ignore_for_file: avoid_dynamic_calls 2 : 3 : import 'package:meedu/provider.dart'; 4 : import 'package:meedu/state.dart'; 5 : 6 : typedef _BuildWhen<S> = bool Function(S prev, S current); 7 : typedef _BuildBySelect<Notifier, Result> = Result Function(Notifier); 8 : 9 : /// enum used to identifier filters used in a provider 10 : enum Filter { 11 : /// .select filter for SimpleProvider and StateProvider 12 : select, 13 : 14 : /// .when filter fot StateProvider 15 : /// this filter only can be used with ref.watch 16 : when, 17 : } 18 : 19 : /// class to save a Notifier, the listener and the rebuild function 20 : class Target<Notifier, Result> extends ListeneableProvider<Notifier> { 21 : // ignore: public_member_api_docs 22 6 : Target(this.notifier); 23 : 24 : /// a SimpleNotifier or a StateNotifier 25 : final Notifier notifier; 26 : 27 : /// listener to listen the changes in our Notifiers 28 : late void Function(dynamic) listener; 29 : 30 : /// function to rebuild the Consumer 31 : void Function()? rebuild; 32 : 33 : /// used to store the value returned by .select or .when 34 : late Result selectValue; 35 : 36 : /// callback defined when a filter is used 37 : late Function callback; 38 : 39 : /// filter type 40 : late Filter filter; 41 : 42 : /// if the filter is a selected and we only want to 43 : /// rebuild the consumer when the callback returns true 44 : late bool listenWhenTheCallbackReturnsTrue; 45 : } 46 : 47 : /// extension for SimpleProvider 48 : extension SimpleProviderExt<Notifier> on SimpleProvider<Notifier> { 49 : /// use this method to rebuild your Consumer when a value has changed 50 : /// or you can use a boolean condition. Check the documentation for more info. 51 : /// 52 : /// [booleanCallback] If your callback returns a boolean and you want to 53 : /// rebuild your consumers or notify to your listeners only when the boolean 54 : /// value is true you can set [booleanCallback] as true 55 : /// 56 : /// EXAMPLE: 57 : /// ```dart 58 : /// final controller = ref.watch( 59 : /// provider.select( 60 : /// (_) => _.user != null, 61 : /// booleanCallback: true, 62 : /// ), 63 : /// ); 64 : /// ``` 65 : /// 66 4 : Target<Notifier, Result> select<Result>( 67 : _BuildBySelect<Notifier, Result> callback, { 68 : bool booleanCallback = false, 69 : }) { 70 : // get the Notifier attached to this SimpleProvider 71 8 : final target = Target<Notifier, Result>(read); 72 4 : target.filter = Filter.select; 73 4 : target.callback = callback; 74 4 : target.listenWhenTheCallbackReturnsTrue = booleanCallback; 75 : return target; 76 : } 77 : } 78 : 79 : /// extension for StateProvider 80 : extension StateProviderExt<Notifier extends StateNotifier<S>, S> 81 : on StateProvider<Notifier, S> { 82 : /// use this method to rebuild your Consumer using the previous state and the current 83 : /// state to return a boolean 84 3 : Target<Notifier, bool> when(_BuildWhen<S> callback) { 85 6 : final target = Target<Notifier, bool>(read); 86 3 : target.filter = Filter.when; 87 3 : target.callback = callback; 88 3 : target.listenWhenTheCallbackReturnsTrue = false; 89 : return target; 90 : } 91 : 92 : /// use this method to rebuild your Consumer when a value in the state has changed 93 : /// or you can use a boolean condition. Check the documentation for more info. 94 : /// 95 : /// [booleanCallback] If your callback returns a boolean and you want to 96 : /// rebuild your consumers or notify to your listeners only when the boolean 97 : /// value is true you can set [booleanCallback] as true 98 : /// 99 : /// EXAMPLE: 100 : /// ```dart 101 : /// final controller = ref.watch( 102 : /// provider.select( 103 : /// (_) => _.user != null, 104 : /// booleanCallback: true, 105 : /// ), 106 : /// ); 107 : /// ``` 108 : /// 109 3 : Target<Notifier, Result> select<Result>( 110 : _BuildBySelect<S, Result> callback, { 111 : bool booleanCallback = false, 112 : }) { 113 6 : final target = Target<Notifier, Result>(read); 114 3 : target.filter = Filter.select; 115 3 : target.callback = callback; 116 3 : target.listenWhenTheCallbackReturnsTrue = booleanCallback; 117 : return target; 118 : } 119 : } 120 : 121 : /// create the listener for provider.select filter (SimpleProvider) 122 4 : void createSimpleSelectListener(Target target) { 123 4 : final notifier = target.notifier; 124 : // get an initial value using the callback 125 8 : dynamic prevValue = target.callback(notifier); 126 4 : target.selectValue = prevValue; 127 : 128 : // listener with the logic to rebuild the Consumer 129 : // ignore: prefer_function_declarations_over_variables 130 4 : final listener = (_) { 131 8 : final value = target.callback(notifier); 132 4 : if (target.listenWhenTheCallbackReturnsTrue) { 133 : assert( 134 6 : value is bool, 135 : 'The value returned by the callback must be a boolean because ' 136 : 'listenWhenTheCallbackReturnsTrue is true', 137 : ); 138 : } 139 : 140 4 : target.selectValue = value; 141 : bool allowRebuild = false; 142 7 : if (value is bool && target.listenWhenTheCallbackReturnsTrue) { 143 : allowRebuild = value; 144 1 : } else if (prevValue != value) { 145 : allowRebuild = true; 146 : } 147 : 148 : // check if the value has changed 149 3 : if (allowRebuild && target.rebuild != null) { 150 6 : target.rebuild!(); // rebuild the Consumer 151 : } 152 : prevValue = value; 153 : }; 154 : // save the listener to be added to our Notifier later 155 4 : target.listener = listener; 156 : } 157 : 158 : /// create the listener for provider.select filter (StateProvider) 159 3 : void createStateSelectListener(Target target) { 160 12 : dynamic prevValue = target.callback(target.notifier.state); 161 3 : target.selectValue = prevValue; 162 : 163 : // ignore: prefer_function_declarations_over_variables 164 3 : final listener = (newState) { 165 12 : final value = target.callback(target.notifier.state); 166 3 : if (target.listenWhenTheCallbackReturnsTrue) { 167 : assert( 168 4 : value is bool, 169 : 'The value returned by the callback must be a boolean because ' 170 : 'listenWhenTheCallbackReturnsTrue is true', 171 : ); 172 : } 173 : 174 3 : target.selectValue = value; 175 : 176 : bool allowRebuild = false; 177 5 : if (value is bool && target.listenWhenTheCallbackReturnsTrue) { 178 : allowRebuild = value; 179 1 : } else if (prevValue != value) { 180 : allowRebuild = true; 181 : } 182 : 183 : // check if the value has changed 184 3 : if (allowRebuild && target.rebuild != null) { 185 6 : target.rebuild!(); 186 : } 187 : prevValue = value; 188 : }; 189 3 : target.listener = listener; 190 : } 191 : 192 : /// create the listener for provider.when filter 193 3 : void createWhenListener(Target target) { 194 3 : final notifier = target.notifier as StateNotifier; 195 15 : target.selectValue = target.callback(notifier.state, notifier.state); 196 : 197 : // ignore: prefer_function_declarations_over_variables 198 3 : final listener = (newState) { 199 : // rebuild the Consumer using the boolean returned by the callback 200 9 : final allowRebuild = target.callback(notifier.oldState, newState); 201 3 : target.selectValue = allowRebuild; 202 : if (allowRebuild) { 203 3 : if (target.rebuild != null) { 204 6 : target.rebuild!(); 205 : } 206 : } 207 : }; 208 3 : target.listener = listener; 209 : }