LCOV - code coverage report
Current view: top level - src/state/widgets/consumer_widget - consumer_widget.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 60 62 96.8 %
Date: 2022-11-04 14:47:16 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:flutter/material.dart';
       2             : import 'package:flutter/widgets.dart';
       3             : import 'package:meedu/meedu.dart';
       4             : 
       5             : import '../watch_filter.dart';
       6             : 
       7             : typedef _ListenerCallback<T> = void Function(T);
       8             : 
       9             : /// A [StatefulWidget] that can read providers.
      10             : abstract class ConsumerStatefulWidget extends StatefulWidget {
      11          10 :   const ConsumerStatefulWidget({Key? key}) : super(key: key);
      12             : 
      13             :   @override
      14             :   // ignore: no_logic_in_create_state
      15             :   ConsumerState createState();
      16             : 
      17           5 :   @override
      18             :   ConsumerStatefulElement createElement() {
      19           5 :     return ConsumerStatefulElement(this);
      20             :   }
      21             : }
      22             : 
      23             : /// A [State] that has access to a [BuilderRef] through [ref], allowing
      24             : /// it to read providers.
      25             : abstract class ConsumerState<T extends ConsumerStatefulWidget>
      26             :     extends State<T> {
      27             :   /// An object that allows widgets to interact with providers.
      28             :   late final BuilderRef ref = context as BuilderRef;
      29             : }
      30             : 
      31             : class ConsumerStatefulElement extends StatefulElement implements BuilderRef {
      32          10 :   ConsumerStatefulElement(StatefulWidget widget) : super(widget);
      33             : 
      34             :   Map<BaseNotifier, _ListenerCallback> _dependencies = {};
      35             :   Map<BaseNotifier, Target> _targets = {};
      36             : 
      37             :   // initialized at true for the first build
      38             :   bool _isExternalBuild = true;
      39             : 
      40             :   bool _mounted = true;
      41             : 
      42           0 :   @override
      43             :   void didChangeDependencies() {
      44           0 :     super.didChangeDependencies();
      45             :     _isExternalBuild = true; // coverage:ignore-line
      46             :   }
      47             : 
      48             :   @override // coverage:ignore-line
      49             :   void reassemble() {
      50             :     super.reassemble(); // coverage:ignore-line
      51             :     _isExternalBuild = true; // coverage:ignore-line
      52             :   }
      53             : 
      54             :   /// force the widget update
      55           4 :   void _rebuild() {
      56           4 :     if (_mounted) {
      57           4 :       markNeedsBuild();
      58             :     }
      59             :   }
      60             : 
      61           5 :   @override
      62             :   void deactivate() {
      63           5 :     _mounted = false;
      64           5 :     _clearDependencies();
      65           5 :     super.deactivate();
      66             :   }
      67             : 
      68             :   /// clear the listeners for this widget
      69           5 :   void _clearDependencies() {
      70          10 :     _dependencies.forEach(
      71           5 :       (notifier, listener) {
      72           5 :         if (!notifier.disposed) {
      73           5 :           (notifier as ListeneableNotifier).removeListener(listener);
      74             :         }
      75             :       },
      76             :     );
      77          10 :     _dependencies = {};
      78          10 :     _targets = {};
      79             :   }
      80             : 
      81             :   /// read a Notifier from one provider and subscribe the widget to the changes of this Notifier.
      82             :   ///
      83             :   /// [providerOrTarget] this param is required to decide when the Consumer
      84             :   /// needs to be rebuilded, if [providerOrTarget] is a [SimpleProvider] or a
      85             :   /// [StateProvider] the  widget will be rebuilded when the notify method is called
      86             :   /// inside a SimpleNotifier or StateNotifier.
      87             :   ///
      88             :   /// If [providerOrTarget] is a value gotten from .select, .ids or .when
      89             :   /// the  widget only will be rebuilded depending of the condition of each method.
      90           5 :   @override
      91             :   Notifier watch<Notifier>(ListeneableProvider<Notifier> providerOrTarget) {
      92             :     // if the widget was rebuilded
      93           5 :     if (_isExternalBuild) {
      94           5 :       _clearDependencies();
      95             :     }
      96           5 :     _isExternalBuild = false;
      97             :     final target =
      98           5 :         providerOrTarget is Target ? providerOrTarget as Target : null;
      99             : 
     100             :     late Notifier notifier;
     101             : 
     102             :     if (target != null) {
     103             :       // If [providerOrTarget] is a value gotten from .select, .ids or .when
     104           3 :       notifier = target.notifier as Notifier;
     105             :     } else {
     106             :       // if [providerOrTarget] is a [SimpleProvider] or a [StateProvider]
     107           4 :       notifier = (providerOrTarget as BaseProvider<Notifier>).read;
     108             :     }
     109             : 
     110          10 :     final insideDependencies = _dependencies.containsKey(notifier);
     111             : 
     112             :     // if there is not a listener for the current provider
     113             :     if (!insideDependencies) {
     114             :       late void Function(dynamic) listener;
     115             :       // if the data passed to the watch function
     116             :       // was gotten using the .ids, .select or .when methods
     117             :       if (target != null) {
     118           6 :         target.rebuild = _rebuild;
     119           3 :         if (notifier is StateNotifier) {
     120           2 :           if (target.filter == Filter.select) {
     121           1 :             createStateSelectListener(target);
     122             :           } else {
     123           1 :             createWhenListener(target);
     124             :           }
     125             :         } else {
     126           2 :           createSimpleSelectListener(target);
     127             :         }
     128           3 :         listener = target.listener;
     129             :       } else {
     130           8 :         listener = (_) => _rebuild();
     131             :       }
     132             :       // add the listener to the current notifier
     133          10 :       _dependencies[notifier as BaseNotifier] = listener;
     134           5 :       (notifier as ListeneableNotifier).addListener(listener);
     135             :     }
     136             :     return notifier; // coverage:ignore-line
     137             :   }
     138             : 
     139             :   /// read a Notifier from one provider and subscribe the widget to the changes of this Notifier.
     140             :   ///
     141             :   /// [target] is a value gotten from .select or .when
     142             :   ///
     143             :   /// the  widget only will be rebuilded depending of the condition of each method.
     144           2 :   @override
     145             :   Result select<Notifier, Result>(Target<Notifier, Result> target) {
     146             :     // if the widget was rebuilded
     147           2 :     if (_isExternalBuild) {
     148           2 :       _clearDependencies();
     149             :     }
     150           2 :     _isExternalBuild = false;
     151           2 :     final notifier = target.notifier;
     152             : 
     153           4 :     final insideDependencies = _dependencies.containsKey(notifier);
     154             :     // if there is not a listener for the current provider
     155             :     if (!insideDependencies) {
     156           4 :       target.rebuild = _rebuild;
     157           2 :       if (notifier is StateNotifier) {
     158           2 :         if (target.filter == Filter.select) {
     159           1 :           createStateSelectListener(target);
     160             :         } else {
     161           1 :           throw FlutterError('.when filter only is allowed with ref.watch');
     162             :         }
     163             :       } else {
     164           1 :         createSimpleSelectListener(target);
     165             :       }
     166             : 
     167           2 :       final listener = target.listener;
     168             :       // add the listener to the current notifier
     169           4 :       _dependencies[notifier as BaseNotifier] = listener;
     170           4 :       _targets[notifier] = target;
     171           2 :       (notifier as ListeneableNotifier).addListener(listener);
     172             :     }
     173             :     return _targets[notifier]!.selectValue; // coverage:ignore-line
     174             :   }
     175             : 
     176           5 :   @override
     177             :   Widget build() {
     178           5 :     return super.build();
     179             :   }
     180             : }
     181             : 
     182             : /// A interface that must be implemented in the ConsumerWidget
     183             : abstract class BuilderRef {
     184             :   /// A function to read SimpleProvider or a StateProvider and subscribe to the events.
     185             :   ///
     186             :   /// this method returns the Notifier linked to the provider
     187             :   T watch<T>(ListeneableProvider<T> providerOrTarget);
     188             : 
     189             :   /// A function to read SimpleProvider or a StateProvider and subscribe to the events.
     190             :   ///
     191             :   /// this method returns the value returned by the select or when methods
     192             :   R select<T, R>(Target<T, R> target);
     193             : }
     194             : 
     195             : /// {@template meedu.consumerwidget}
     196             : /// A base-class for widgets that wants to listen to providers
     197             : /// ```dart
     198             : /// class Example extends ConsumerWidget {
     199             : ///   const Example({Key? key}): super(key: key);
     200             : ///
     201             : ///   @override
     202             : ///   Widget build(BuildContext context, ref) {
     203             : ///     final value = ref.watch(myProvider);
     204             : ///     return YOUR_WIDGET;
     205             : ///   }
     206             : /// }
     207             : /// ```
     208             : /// {@endtemplate}
     209             : abstract class ConsumerWidget extends ConsumerStatefulWidget {
     210             :   // ignore: public_member_api_docs
     211          10 :   const ConsumerWidget({Key? key}) : super(key: key);
     212             : 
     213             :   // ignore: public_member_api_docs
     214             :   Widget build(BuildContext context, BuilderRef ref);
     215             : 
     216           5 :   @override
     217           5 :   _ConsumerState createState() => _ConsumerState();
     218             : }
     219             : 
     220             : class _ConsumerState<T extends ConsumerWidget>
     221             :     extends ConsumerState<ConsumerWidget> {
     222             :   /// An object that allows widgets to interact with providers.
     223          10 :   BuilderRef get ref => context as BuilderRef;
     224             : 
     225           5 :   @override
     226             :   Widget build(BuildContext context) {
     227          15 :     return widget.build(context, ref);
     228             :   }
     229             : }

Generated by: LCOV version 1.16