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

          Line data    Source code
       1             : import 'package:flutter/material.dart';
       2             : import 'package:meedu/provider.dart';
       3             : import 'package:meedu/state.dart';
       4             : 
       5             : import '../../utils/ambiguate.dart';
       6             : import '../widgets/watch_filter.dart';
       7             : 
       8             : typedef _ListenerCallback<T> = void Function(T);
       9             : typedef _ProviderListenerCallback<Notifier> = void Function(
      10             :   BuildContext,
      11             :   Notifier,
      12             : );
      13             : 
      14             : /// this class allows you listen the changes in multiples providers
      15             : class MultiProviderListener extends StatefulWidget {
      16             :   // ignore: public_member_api_docs
      17           1 :   MultiProviderListener({
      18             :     Key? key,
      19             :     required this.items,
      20             :     required this.child,
      21             :     this.onInitState,
      22             :     this.onAfterFirstLayout,
      23             :     this.onDispose,
      24           1 :   }) : super(key: key);
      25             : 
      26             :   /// a list of [MultiProviderListenerItem]
      27             :   final List<MultiProviderListenerItem> items;
      28             : 
      29             :   /// callback when initState is called
      30             :   final void Function(BuildContext)? onInitState;
      31             : 
      32             :   /// this callback will be called when the first frame was rendered
      33             :   /// use this callback if you want to show a dialog, snackbar or navigate
      34             :   /// after the first frame
      35             :   final void Function(BuildContext)? onAfterFirstLayout;
      36             : 
      37             :   /// callback when dispose is called
      38             :   final void Function(BuildContext)? onDispose;
      39             : 
      40             :   /// widget to be rendered
      41             :   final Widget child;
      42             : 
      43           1 :   @override
      44           1 :   _MultiProviderListenerState createState() => _MultiProviderListenerState();
      45             : }
      46             : 
      47             : class _MultiProviderListenerState extends State<MultiProviderListener> {
      48             :   Map<BaseNotifier, List<_ListenerCallback>> _dependencies = {};
      49             : 
      50           1 :   @override
      51             :   void initState() {
      52           1 :     super.initState();
      53           1 :     _addListeners();
      54             :     // check if the onInitState callback needs to be called
      55           2 :     if (widget.onInitState != null) {
      56           4 :       widget.onInitState!(context);
      57             :     }
      58             : 
      59             :     // check if the onAfterFirstLayout callback needs to be called
      60           2 :     if (widget.onAfterFirstLayout != null) {
      61             :       // wait after first frame
      62           5 :       ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) {
      63           1 :         if (mounted) {
      64           4 :           widget.onAfterFirstLayout!(context);
      65             :         }
      66             :       });
      67             :     }
      68             :   }
      69             : 
      70           1 :   void _addListeners() {
      71             :     /// read all providers passed into widget.items
      72             :     /// and add a listener when a notifier changes
      73           3 :     for (final item in widget.items) {
      74           3 :       final target = item.provider is Target ? item.provider as Target : null;
      75             :       final BaseNotifier notifier = target != null
      76           1 :           ? target.notifier
      77           2 :           : (item.provider as BaseProvider).read;
      78             : 
      79           2 :       if (!_dependencies.containsKey(notifier)) {
      80           3 :         _dependencies[notifier] = [];
      81             :       }
      82             : 
      83             :       if (target != null) {
      84           2 :         target.rebuild = () {
      85           1 :           if (mounted) {
      86             :             // ignore: avoid_dynamic_calls
      87           2 :             (item as dynamic).onChange(context, notifier);
      88             :           }
      89             :         };
      90             : 
      91           1 :         if (notifier is SimpleNotifier) {
      92           2 :           if (target.filter == Filter.select) {
      93           1 :             createSimpleSelectListener(target);
      94             :           }
      95             :         } else {
      96           2 :           if (target.filter == Filter.select) {
      97           1 :             createStateSelectListener(target);
      98             :           } else {
      99           1 :             createWhenListener(target);
     100             :           }
     101             :         }
     102           1 :         final listener = target.listener;
     103           1 :         (notifier as ListeneableNotifier).addListener(listener);
     104           3 :         _dependencies[notifier]?.add(listener);
     105             :       } else {
     106             :         // ignore: prefer_function_declarations_over_variables
     107           1 :         final listener = (_) {
     108             :           // before call onChange we need to check
     109             :           // if the widget is mounted
     110           1 :           if (mounted) {
     111             :             // ignore: avoid_dynamic_calls
     112           2 :             (item as dynamic).onChange(context, notifier);
     113             :           }
     114             :         };
     115           1 :         (notifier as ListeneableNotifier).addListener(listener);
     116           3 :         _dependencies[notifier]?.add(listener);
     117             :       }
     118             :     }
     119             :   }
     120             : 
     121             :   /// clear the listeners for this widget
     122           1 :   void _clearDependencies() {
     123           3 :     for (final item in _dependencies.entries) {
     124           1 :       final notifier = item.key;
     125           1 :       if (!notifier.disposed) {
     126             :         // ignore: prefer_foreach
     127           2 :         for (final listener in item.value) {
     128           1 :           (notifier as ListeneableNotifier).removeListener(listener);
     129             :         }
     130             :       }
     131             :     }
     132           2 :     _dependencies = {};
     133             :   }
     134             : 
     135             :   /// listen when the widget is updated
     136             :   /// due to the properties has changes or for hot reaload
     137           1 :   @override
     138             :   void didUpdateWidget(MultiProviderListener oldWidget) {
     139           1 :     _clearDependencies();
     140           1 :     _addListeners();
     141           1 :     super.didUpdateWidget(oldWidget);
     142             :   }
     143             : 
     144             :   /// if the widget will be disposed
     145           1 :   @override
     146             :   void deactivate() {
     147           1 :     _clearDependencies();
     148           1 :     super.deactivate();
     149             :   }
     150             : 
     151           1 :   @override
     152             :   Widget build(BuildContext context) {
     153           2 :     return widget.child;
     154             :   }
     155             : }
     156             : 
     157             : /// this class is used to define onChange callback for one Notifier
     158             : class MultiProviderListenerItem<Notifier extends BaseNotifier> {
     159             :   // ignore: public_member_api_docs
     160           1 :   MultiProviderListenerItem({
     161             :     required this.provider,
     162             :     required this.onChange,
     163             :   });
     164             : 
     165             :   /// provider to listen the changes
     166             :   final ListeneableProvider<Notifier> provider;
     167             : 
     168             :   /// callback to listen the new events
     169             :   final _ProviderListenerCallback<Notifier> onChange;
     170             : }

Generated by: LCOV version 1.16