LCOV - code coverage report
Current view: top level - lib - property_change_provider.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 43 43 100.0 %
Date: 2019-09-01 07:51:50 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] subclass.
       6             : ///
       7             : /// A descendant widget can access the model instance by using the following syntax.
       8             : /// This will automatically register the widget to be rebuilt whenever any property changes on the model:
       9             : /// ```dart
      10             : /// final model = PropertyChangeProvider.of<MyModel>(context).value;
      11             : /// ```
      12             : ///
      13             : /// To access the property name that was changed, use the following syntax:
      14             : /// ```dart
      15             : /// final property = PropertyChangeProvider.of<MyModel>(context).property;
      16             : /// ```
      17             : ///
      18             : /// To register the widget to be rebuilt only on specific property changes, provide a [properties] parameter:
      19             : /// ```dart
      20             : /// final model = PropertyChangeProvider.of<MyModel>(context, properties: ['foo', 'bar']).value;
      21             : /// ```
      22             : ///
      23             : /// To access the model without registering the widget to be rebuilt, provide a [listen] parameter with a value of false:
      24             : /// ```dart
      25             : /// final model = PropertyChangeProvider.of<MyModel>(context, listen: false).value;
      26             : /// ```
      27             : class PropertyChangeProvider<T extends PropertyChangeNotifier> extends StatefulWidget {
      28             :   /// Retrieves the [PropertyChangeModel] from the nearest ancestor [PropertyChangeProvider].
      29             :   /// If [listen] is true (which is the default), the calling widget will also be rebuilt
      30             :   /// whenever the ancestor's [PropertyChangeNotifier] model changes. To only rebuild
      31             :   /// for certain properties, provide them in the [properties] parameter.
      32             :   /// If [listen] is false, the [properties] parameter must be null or empty.
      33           2 :   static PropertyChangeModel<T> of<T extends PropertyChangeNotifier>(
      34             :     BuildContext context, {
      35             :     Iterable<Object> properties,
      36             :     bool listen = true,
      37             :   }) {
      38           1 :     assert(listen || properties == null, "Don't provide properties if you're not going to listen to them.");
      39             : 
      40           1 :     final typeOf = <T>() => T;
      41             : 
      42           2 :     final nullCheck = (InheritedModel model) {
      43           4 :       assert(model != null, 'Could not find an ancestor PropertyChangeProvider<$T>');
      44             :       return model;
      45             :     };
      46             : 
      47             :     if (!listen) {
      48           1 :       final type = typeOf<PropertyChangeModel<T>>();
      49           2 :       return nullCheck(context.ancestorWidgetOfExactType(type) as PropertyChangeModel);
      50             :     }
      51             : 
      52           2 :     if (properties == null || properties.isEmpty) {
      53           4 :       return nullCheck(InheritedModel.inheritFrom<PropertyChangeModel<T>>(context));
      54             :     }
      55             : 
      56             :     InheritedModel widget;
      57           4 :     for (final property in properties) {
      58           2 :       widget = InheritedModel.inheritFrom<PropertyChangeModel<T>>(context, aspect: property);
      59             :     }
      60             : 
      61           2 :     return nullCheck(widget);
      62             :   }
      63             : 
      64             :   /// Creates a [PropertyChangeProvider] that can be accessed by descendant widgets.
      65           2 :   PropertyChangeProvider({
      66             :     Key key,
      67             :     @required this.value,
      68             :     @required this.child,
      69           1 :   })  : assert(value != null),
      70           1 :         assert(child != null),
      71           2 :         super(key: key);
      72             : 
      73             :   /// The instance of [T] to provide to descendant widgets.
      74             :   final T value;
      75             : 
      76             :   /// The widget below this widget in the tree.
      77             :   ///
      78             :   /// {@macro flutter.widgets.child}
      79             :   final Widget child;
      80             : 
      81           2 :   @override
      82           2 :   _PropertyChangeProviderState createState() => _PropertyChangeProviderState<T>();
      83             : }
      84             : 
      85             : /// The companion [State] object to [PropertyChangeProvider]. For private use only.
      86             : /// Subscribes as a global listener to the [PropertyChangeNotifier] instance at [widget].[value].
      87             : /// Rebuilds whenever a property is changed and creates a new [PropertyChangeModel] with a reference
      88             : /// to itself so it can access the original model instance and newly changed property name.
      89             : class _PropertyChangeProviderState<T extends PropertyChangeNotifier> extends State<PropertyChangeProvider<T>> {
      90             :   Object _property;
      91             : 
      92           2 :   @override
      93             :   void initState() {
      94           2 :     super.initState();
      95           8 :     widget.value.addListener(_listener);
      96             :   }
      97             : 
      98           2 :   @override
      99             :   void dispose() {
     100           8 :     widget.value.removeListener(_listener);
     101           2 :     super.dispose();
     102             :   }
     103             : 
     104           2 :   @override
     105             :   Widget build(BuildContext context) {
     106           2 :     return PropertyChangeModel<T>(
     107             :       state: this,
     108           4 :       child: widget.child,
     109             :     );
     110             :   }
     111             : 
     112           2 :   void _listener(Object property) {
     113           4 :     setState(() {
     114           2 :       _property = property;
     115             :     });
     116             :   }
     117             : }
     118             : 
     119             : /// The [InheritedModel] subclass that is rebuilt by [_PropertyChangeProviderState]
     120             : /// whenever its [PropertyChangeNotifier] is updated. Notifies dependents when the
     121             : /// name of the changed property is contained in the list of properties provided
     122             : /// when the widgets originally called the [PropertyChangeProvider].[of] method.
     123             : /// The type parameter [T] is the type of the [PropertyChangeNotifier] subclass.
     124             : class PropertyChangeModel<T extends PropertyChangeNotifier> extends InheritedModel {
     125             :   final _PropertyChangeProviderState _state;
     126             : 
     127           2 :   PropertyChangeModel({
     128             :     Key key,
     129             :     _PropertyChangeProviderState state,
     130             :     Widget child,
     131             :   })  : _state = state,
     132           2 :         super(key: key, child: child);
     133             : 
     134             :   /// The instance of [T] originally provided to the [PropertyChangeProvider] constructor.
     135           8 :   T get value => _state.widget.value;
     136             : 
     137             :   /// The name of the property that was last changed on the [value] instance.
     138           6 :   Object get property => _state._property;
     139             : 
     140           2 :   @override
     141             :   bool updateShouldNotify(PropertyChangeModel oldWidget) {
     142             :     return true;
     143             :   }
     144             : 
     145           2 :   @override
     146             :   bool updateShouldNotifyDependent(PropertyChangeModel<T> oldWidget, Set<Object> aspects) {
     147           6 :     return aspects.contains(_state._property);
     148             :   }
     149             : }
     150             : 
     151             : /// A widget-based listener for cases where a [BuildContext] is hard to access, or if you prefer this kind of API.
     152             : /// To register the widget to be rebuilt only on specific property changes, provide a [properties] parameter.
     153             : ///
     154             : /// Access both the model value and the changed property via the [builder] callback:
     155             : /// ```dart
     156             : /// PropertyChangeConsumer<MyModel>(
     157             : ///    properties: ['foo', 'bar'],
     158             : ///    builder: (context, model, property) {
     159             : ///      return Column(
     160             : ///        children: [
     161             : ///          Text('$property was changed!'),
     162             : ///          RaisedButton(
     163             : ///            child: Text('Update foo'),
     164             : ///            onPressed: () {
     165             : ///              model.foo = DateTime.now().toString();
     166             : ///            },
     167             : ///          ),
     168             : ///          RaisedButton(
     169             : ///            child: Text('Update bar'),
     170             : ///            onPressed: () {
     171             : ///              model.bar = DateTime.now().toString();
     172             : ///            },
     173             : ///          ),
     174             : ///        ],
     175             : ///      );
     176             : ///    },
     177             : ///  );
     178             : /// ```
     179             : class PropertyChangeConsumer<T extends PropertyChangeNotifier> extends StatelessWidget {
     180             :   final Iterable<Object> properties;
     181             :   final Widget Function(BuildContext, T, Object) builder;
     182             : 
     183           1 :   PropertyChangeConsumer({
     184             :     Key key,
     185             :     this.properties,
     186             :     @required this.builder,
     187           1 :   })  : assert(builder != null),
     188           1 :         super(key: key);
     189             : 
     190           1 :   @override
     191             :   Widget build(BuildContext context) {
     192           2 :     final model = PropertyChangeProvider.of<T>(context, properties: this.properties, listen: true);
     193           3 :     return this.builder(context, model.value, model.property);
     194             :   }
     195             : }

Generated by: LCOV version 1.14