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 : }