LCOV - code coverage report
Current view: top level - src - property_change_provider.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 38 38 100.0 %
Date: 2021-09-17 16:22:12 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:flutter/widgets.dart';
       2             : import 'package:property_change_notifier/property_change_notifier.dart';
       3             : 
       4             : /// An [InheritedWidget] that provides access to a [PropertyChangeNotifier] to descendant widgets.
       5             : /// The type parameter [T] is the type of the [PropertyChangeNotifier<S>] subclass.
       6             : /// The type parameter [S] is the type of the properties to observe.
       7             : ///
       8             : /// Given the following model:
       9             : /// ```dart
      10             : /// class MyModel extends PropertyChangeNotifier<String> {...}
      11             : /// ```
      12             : ///
      13             : /// A descendant widget can access the model instance by using the following syntax.
      14             : /// This will automatically register the widget to be rebuilt whenever any property changes on the model:
      15             : /// ```dart
      16             : /// final model = PropertyChangeProvider.of<MyModel, String>(context).value;
      17             : /// ```
      18             : ///
      19             : /// To access the properties that were changed in the current build frame, use the following syntax.
      20             : /// ```dart
      21             : /// final properties = PropertyChangeProvider.of<MyModel, String>(context).properties;
      22             : /// ```
      23             : ///
      24             : /// To register the widget to be rebuilt only on specific property changes, provide a [properties] parameter:
      25             : /// ```dart
      26             : /// final model = PropertyChangeProvider.of<MyModel, String>(context, properties: ['foo', 'bar']).value;
      27             : /// ```
      28             : ///
      29             : /// To only access the model without registering the widget to be rebuilt, provide a [listen] parameter with a value of false:
      30             : /// ```dart
      31             : /// final model = PropertyChangeProvider.of<MyModel, String>(context, listen: false).value;
      32             : /// ```
      33             : class PropertyChangeProvider<T extends PropertyChangeNotifier<S>, S extends Object> extends StatefulWidget {
      34             :   /// Retrieves the [PropertyChangeModel] from the nearest ancestor [PropertyChangeProvider].
      35             :   /// If [listen] is true (which is the default), the calling widget will also be rebuilt
      36             :   /// whenever the ancestor's [PropertyChangeNotifier] model changes. To only rebuild
      37             :   /// for certain properties, provide them in the [properties] parameter.
      38             :   /// If [listen] is false, the [properties] parameter must be null or empty.
      39           2 :   static PropertyChangeModel<T, S>? of<T extends PropertyChangeNotifier<S>, S extends Object>(
      40             :     BuildContext context, {
      41             :     Iterable<S>? properties,
      42             :     bool listen = true,
      43             :   }) {
      44           1 :     assert(listen || properties == null, "Don't provide properties if you're not going to listen to them.");
      45             : 
      46           2 :     PropertyChangeModel<T, S>? nullCheck(PropertyChangeModel<T, S>? model) {
      47           4 :       assert(model != null, 'Could not find an ancestor PropertyChangeProvider<$T, $S>');
      48             :       return model;
      49             :     };
      50             : 
      51             :     if (!listen) {
      52           2 :       return nullCheck(context.findAncestorWidgetOfExactType<PropertyChangeModel<T, S>>());
      53             :     }
      54             : 
      55           2 :     if (properties == null || properties.isEmpty) {
      56           4 :       return nullCheck(context.dependOnInheritedWidgetOfExactType<PropertyChangeModel<T, S>>());
      57             :     }
      58             : 
      59             :     PropertyChangeModel<T, S>? widget;
      60           4 :     for (final property in properties) {
      61           2 :       widget = InheritedModel.inheritFrom<PropertyChangeModel<T, S>>(context, aspect: property);
      62             :     }
      63             : 
      64           2 :     return nullCheck(widget);
      65             :   }
      66             : 
      67             :   /// Creates a [PropertyChangeProvider] that can be accessed by descendant widgets.
      68           2 :   const PropertyChangeProvider({
      69             :     Key? key,
      70             :     required this.value,
      71             :     required this.child,
      72           2 :   })  : super(key: key);
      73             : 
      74             :   /// The instance of [T] to provide to descendant widgets.
      75             :   final T value;
      76             : 
      77             :   /// The widget below this widget in the tree.
      78             :   ///
      79             :   /// {@macro flutter.widgets.child}
      80             :   final Widget child;
      81             : 
      82           2 :   @override
      83           2 :   _PropertyChangeProviderState createState() => _PropertyChangeProviderState<T, S>();
      84             : }
      85             : 
      86             : /// A convenience typedef to use in the common use case where property names are of type [String].
      87             : typedef StringPropertyChangeProvider<T extends PropertyChangeNotifier<String>> = PropertyChangeProvider<T, String>;
      88             : 
      89             : /// The companion [State] object to [PropertyChangeProvider]. For private use only.
      90             : /// Subscribes as a global listener to the [PropertyChangeNotifier] instance at [widget].[value].
      91             : /// Rebuilds whenever a property is changed and creates a new [PropertyChangeModel] with a reference
      92             : /// to itself so it can access the original model instance and changed property names.
      93             : class _PropertyChangeProviderState<T extends PropertyChangeNotifier<S>, S extends Object> extends State<PropertyChangeProvider<T, S>> {
      94             :   Set<S> _properties = {};
      95             : 
      96           2 :   @override
      97             :   void initState() {
      98           2 :     super.initState();
      99           8 :     widget.value.addListener(_listener);
     100             :   }
     101             : 
     102           2 :   @override
     103             :   void dispose() {
     104           8 :     widget.value.removeListener(_listener);
     105           2 :     super.dispose();
     106             :   }
     107             : 
     108           2 :   @override
     109             :   Widget build(BuildContext context) {
     110           2 :     return PropertyChangeModel<T, S>(
     111             :       state: this,
     112           4 :       child: widget.child,
     113             :     );
     114             :   }
     115             : 
     116           2 :   void _listener(S? property) {
     117           4 :     setState(() {
     118           2 :       _addProperty(property);
     119             :     });
     120             :   }
     121             : 
     122           2 :   void _addProperty(S? property) {
     123             :     if (property == null) return;
     124           2 :     final element = context as StatefulElement;
     125           2 :     if (element.dirty) {
     126           2 :       _properties.add(property);
     127             :     } else {
     128           4 :       _properties = {property};
     129             :     }
     130             :   }
     131             : }
     132             : 
     133             : /// The [InheritedModel] subclass that is rebuilt by [_PropertyChangeProviderState]
     134             : /// whenever its [PropertyChangeNotifier] is updated. Notifies dependents when the
     135             : /// names of the changed properties intersect with the list of properties provided
     136             : /// to the [PropertyChangeProvider].[of] method.
     137             : /// The type parameter [T] is the type of the [PropertyChangeNotifier<S>] subclass.
     138             : /// The type parameter [S] is the type of the properties to observe.
     139             : class PropertyChangeModel<T extends PropertyChangeNotifier<S>, S extends Object> extends InheritedModel<S> {
     140             :   final _PropertyChangeProviderState<T, S> _state;
     141             : 
     142           2 :   const PropertyChangeModel({
     143             :     Key? key,
     144             :     required _PropertyChangeProviderState<T, S> state,
     145             :     required Widget child,
     146             :   })  : _state = state,
     147           2 :         super(key: key, child: child);
     148             : 
     149             :   /// The instance of [T] originally provided to the [PropertyChangeProvider] constructor.
     150           8 :   T get value => _state.widget.value;
     151             : 
     152             :   /// The names of the properties on the [value] instance that were changed in the current build frame.
     153           6 :   Set<S> get properties => _state._properties;
     154             : 
     155           2 :   @override
     156             :   bool updateShouldNotify(PropertyChangeModel oldWidget) {
     157             :     return true;
     158             :   }
     159             : 
     160           2 :   @override
     161             :   bool updateShouldNotifyDependent(PropertyChangeModel<T, S> oldWidget, Set<S> aspects) {
     162           8 :     return aspects.intersection(_state._properties).isNotEmpty;
     163             :   }
     164             : }

Generated by: LCOV version 1.14