diff --git a/lib/composition.dart b/lib/composition.dart index 6a5757c..7b892f1 100644 --- a/lib/composition.dart +++ b/lib/composition.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'inheritable.dart'; -bool _equals({Object prev, Object next}) { +bool _equals({required Object? prev, required Object? next}) { return next != prev; } @@ -25,19 +25,16 @@ PredicateAspect debounce( PredicateAspect shouldNotify = _equals, bool leading = false, }) { - assert(leading != null); - assert(shouldNotify != null); - assert(duration != null); bool _exhausted = false; - T _lastValue; + T? _lastValue; - Timer timer; + Timer? timer; void stop() { _exhausted = true; timer = null; } - return ({T prev, T next}) { + return ({required T prev, required T next}) { _lastValue ??= leading ? next : prev; /// After notifying for leading change, stop notifying for it @@ -58,7 +55,7 @@ PredicateAspect debounce( // Allows restarting timer for next run _exhausted = false; - if (shouldNotify(next: next, prev: _lastValue)) { + if (shouldNotify(next: next, prev: _lastValue!)) { _lastValue = next; return true; } diff --git a/lib/extensions.dart b/lib/extensions.dart index 55f2d8c..ad5887d 100644 --- a/lib/extensions.dart +++ b/lib/extensions.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; import 'inheritable.dart'; extension BoolAspectOf on Aspect { - Aspect map({R onTrue, R onFalse, Key key}) { + Aspect map({required R onTrue, required R onFalse, Key? key}) { return AspectChianingFn(this).map( (b) => b ? onTrue : onFalse, key, diff --git a/lib/inheritable.dart b/lib/inheritable.dart index 7b78785..d957531 100644 --- a/lib/inheritable.dart +++ b/lib/inheritable.dart @@ -14,12 +14,12 @@ export 'extensions.dart'; typedef ExtractAspect = A Function(T it); /// Given [T] return whether you should be notified or not. -typedef PredicateAspect = bool Function({T prev, T next}); +typedef PredicateAspect = bool Function({required T prev, required T next}); /// Provides a default implementation of [DependableAspect.didUpdateWidget] mixin ShouldNotifyAspect on DependableAspect { @override - didUpdateWidget({next, prev}) { + didUpdateWidget({required next, required prev}) { return shouldNotify(next.valueFor(this), prev.valueFor(this)); } @@ -36,7 +36,7 @@ mixin TransformingAspect on InheritableAspect { A transform(T value); @override - A of(BuildContext context, {rebuild = true}); + A? of(BuildContext context, {rebuild = true}); } /// An [InheritableAspect] that allows providing a defaultValue in it's [of] method @@ -49,7 +49,7 @@ mixin DefaultAspectofContext on TransformingAspect { /// no value could be produced. /// {@endtemplate} @override - A of(BuildContext context, {rebuild = true, A defaultValue}); + A? of(BuildContext context, {rebuild = true, A? defaultValue}); } abstract class AspectOverride { @@ -93,8 +93,7 @@ class _AspectOverrideMutation> final InheritableAspect aspect; const _AspectOverrideMutation(this.aspect, A override) - : assert(aspect != null), - super.constant(override); + : super.constant(override); @override get hashCode => aspect.hashCode; @@ -110,8 +109,7 @@ class _AspectOverrideByEquality extends AspectOverride { final InheritableAspect aspect; const _AspectOverrideByEquality(this.aspect, A override) - : assert(aspect != null), - assert(aspect is! MutableInheritableAspect, + : assert(aspect is! MutableInheritableAspect, 'Prefer using AspectOverride.mutation instead.'), super.constant(override); @@ -132,9 +130,7 @@ class _AspectOverrideByKey extends AspectOverride { final bool mutation; const _AspectOverrideByKey(this.key, A override, {this.mutation = false}) - : assert(key != null), - assert(mutation != null), - super.constant(override); + : super.constant(override); @override get hashCode => key.hashCode; @@ -156,22 +152,22 @@ abstract class InheritableAspect with Diagnosticable { /// object identity. /// /// Same key can also be used to replace an aspect used by a widget. - Key get key; + Key? get key; /// Debug-mode label for this aspect - final String debugLabel; + final String? debugLabel; /// Constant constructor for subclasses const InheritableAspect([this.debugLabel]); /// {@macro InheritableAspect.none} - static NoAspect none([Key key]) => NoAspect(key); + static NoAspect none([Key? key]) => NoAspect(key); /// Create an aspect of [T] that is of type [A] /// /// The provided function will be provided [T] and it should take only 1 /// aspect from it. - static Aspect extract(ExtractAspect extract, [Key key]) => + static Aspect extract(ExtractAspect extract, [Key? key]) => Aspect(extract, key); /// Specify whether given [inheritable] satisfies this aspect. @@ -198,7 +194,7 @@ abstract class InheritableAspect with Diagnosticable { /// /// The default implementation returns the nearest enclosing [Inheritable] of /// [T] satisfying this aspect or `null` - Object of(BuildContext context, {bool rebuild = true}) { + Object? of(BuildContext context, {bool rebuild = true}) { return Inheritable.of(context, aspect: this, rebuild: rebuild); } @@ -287,12 +283,19 @@ mixin DependableAspect on InheritableAspect { /// This is similar to `copyWith` method pattern although intended only for internal use. @visibleForOverriding @protected - InheritableAspect ensureHasKey({Key fallback}); + InheritableAspect ensureHasKey({Key? fallback}); + + /// If no key is specified for [InheritableAspect] or the depending widget, + /// this key will be assigned to aspects for a given [BuildContext] + static const defaultKey = Key('DependableAspect.defaultKey'); /// Called by [Inheritable] of [T] when it decides to notify it's dependents. /// This is only called after [Inheritable] of [T] has been updated at least /// once. For the first time, aka "init" phase, [satisfiedBy] is called instead. - bool didUpdateWidget({Inheritable next, Inheritable prev}); + bool didUpdateWidget({ + required Inheritable next, + required Inheritable prev, + }); @override @visibleForOverriding @@ -316,7 +319,7 @@ mixin ClonableAspect on InheritableAspect { /// /// Implementations of this method are expected to return `this` if no fields /// are replaced. - InheritableAspect clone({Key key}); + InheritableAspect clone({Key? key}); } /// Provides default implementations for [Object.==] && [Object.hashCode]. @@ -329,7 +332,7 @@ mixin ClonableAspect on InheritableAspect { /// This library internally uses [_hash] && [_equals] instead of /// [Object.hashCode] && [Object.==] to force consistency. abstract class EquatableAspect extends InheritableAspect { - const EquatableAspect([String debugLabel]) : super(debugLabel); + const EquatableAspect([String? debugLabel]) : super(debugLabel); static final Expando _cache = Expando('InheritableAspect.hashCode'); @@ -361,7 +364,7 @@ mixin DelegatingAspect on ClonableAspect { InheritableAspect get delegate; @override - Key get key => delegate.key; + Key? get key => delegate.key; @override bool satisfiedBy(Inheritable inheritable) { @@ -381,7 +384,7 @@ mixin DelegatingAspect on ClonableAspect { /// /// This will throw [UnsupportedError] if [delegate] or [replacement] isn't [ClonableAspect] InheritableAspect cloneDelegate( - {Key key, InheritableAspect replacement}) { + {Key? key, InheritableAspect? replacement}) { if ([key, replacement].whereType().isEmpty) return delegate; final resolved = replacement ?? delegate; @@ -392,7 +395,7 @@ mixin DelegatingAspect on ClonableAspect { } @override - DelegatingAspect clone({Key key, InheritableAspect delegate}); + DelegatingAspect clone({Key? key, InheritableAspect? delegate}); @override of(context, {rebuild = true}) => @@ -430,7 +433,7 @@ mixin DelegatingDependableAspect bool predicate(Inheritable inheritable) => true; @override - bool didUpdateWidget({Inheritable next, Inheritable prev}) { + bool didUpdateWidget({required next, required prev}) { if (predicate(next)) { final dynamic delegate = this.delegate; @@ -442,7 +445,7 @@ mixin DelegatingDependableAspect } @override - DelegatingAspect ensureHasKey({Key fallback}) { + DelegatingAspect ensureHasKey({Key? fallback}) { final dynamic delegate = this.delegate; if (delegate is DependableAspect) return clone(delegate: delegate.ensureHasKey(fallback: fallback)); @@ -453,7 +456,7 @@ mixin DelegatingDependableAspect /// Convenience method to create a new copy of [delegate] with key. This is /// useful in [clone] implementations where [key] is always delegated to [delegate]. InheritableAspect ensureDelegateHasKey( - [InheritableAspect replacement, Key key]) { + [InheritableAspect? replacement, Key? key]) { final dynamic delegate = replacement ?? this.delegate; if (delegate is DependableAspect) return delegate.ensureHasKey(fallback: key); @@ -478,9 +481,7 @@ class _ByInheritableAspect extends EquatableAspect final InheritableAspectSatisfied _predicate; _ByInheritableAspect(this.delegate, this._predicate) - : assert(delegate != null), - assert(_predicate != null), - super('_ByInheritableAspect of ${delegate.debugLabel}'); + : super('_ByInheritableAspect of ${delegate.debugLabel}'); @override bool predicate(Inheritable inheritable) { @@ -508,9 +509,9 @@ class _ByInheritableAspect extends EquatableAspect @override _ByInheritableAspect clone({ - Key key, - InheritableAspect delegate, - InheritableAspectSatisfied predicate, + Key? key, + InheritableAspect? delegate, + InheritableAspectSatisfied? predicate, }) { if ([key, delegate, predicate].whereType().isEmpty) return this; @@ -566,9 +567,9 @@ extension InheritableAspectChainable on DependableAspect { /// Use [mapper] to be notified for [T] when it /// s mapped value changes /// {@endtemplate} - Aspect map(R Function(T) mapper, [Key key]) { + Aspect map(R Function(T) mapper, [Key? key]) { return Aspect._( - ({next, prev, aspect}) => + ({required next, required prev, required aspect}) => didUpdateWidget(prev: prev, next: next) && next.valueFor(aspect, mapper) != prev.valueFor(aspect, mapper), mapper, @@ -576,9 +577,10 @@ extension InheritableAspectChainable on DependableAspect { ); } - Aspect withPatch(AspectPatch patch, [Key key]) { + Aspect withPatch(AspectPatch patch, [Key? key]) { return Aspect._( - ({next, prev, aspect}) => didUpdateWidget(prev: prev, next: next), + ({required next, required prev, required aspect}) => + didUpdateWidget(prev: prev, next: next), (t) => t, key ?? this.key, null, @@ -587,9 +589,10 @@ extension InheritableAspectChainable on DependableAspect { } /// Add default value to this, if no satisfiable [Inheritable] of [T] can be found - Aspect withDefault(T value, [Key key]) { + Aspect withDefault(T value, [Key? key]) { return Aspect._( - ({next, prev, aspect}) => didUpdateWidget(prev: prev, next: next), + ({required next, required prev, required aspect}) => + didUpdateWidget(prev: prev, next: next), (t) => t, key ?? this.key, (_) => value, @@ -598,9 +601,10 @@ extension InheritableAspectChainable on DependableAspect { /// Add default value to this based on provided [BuildContext], if no satisfiable [Inheritable] of [T] can be found. Aspect withDefaultFor(DefaultInheritableAspectOfContext fn, - [Key key]) { + [Key? key]) { return Aspect._( - ({next, prev, aspect}) => didUpdateWidget(prev: prev, next: next), + ({required next, required prev, required aspect}) => + didUpdateWidget(prev: prev, next: next), (t) => t, key ?? this.key, fn, @@ -610,9 +614,9 @@ extension InheritableAspectChainable on DependableAspect { /// {@template InheritableAspect.where} /// Use [predicate] whether to be notified for [T] /// {@endtemplate} - Aspect where(PredicateAspect predicate, [Key key]) { + Aspect where(PredicateAspect predicate, [Key? key]) { return Aspect._( - ({next, prev, aspect}) => + ({required next, required prev, required aspect}) => didUpdateWidget(prev: prev, next: next) && predicate(next: next.valueFor(aspect), prev: prev.valueFor(aspect)), (it) => it, @@ -623,9 +627,9 @@ extension InheritableAspectChainable on DependableAspect { /// {@template InheritableAspect.whereType} /// Notify for [T] only if it's also [R] /// {@endtemplate} - Aspect whereType([Key key]) { + Aspect whereType([Key? key]) { return Aspect._( - ({prev, next, aspect}) => + ({required prev, required next, required aspect}) => didUpdateWidget(prev: prev, next: next) && (next.valueFor(aspect) is R), (t) => AspectChianingFn._whereType(t, (t) => t), @@ -648,12 +652,12 @@ extension InheritableAspectChainable on DependableAspect { /// The created [AspectOverride] depends on `this.key`, which if `null` it will throw. /// {@endtemplate} AspectOverride, T> operator <(ValueChanged onMutate) { - return AspectOverride.key(key, onMutate, mutation: true); + return AspectOverride.key(key!, onMutate, mutation: true); } /// Returns an [InheritableAspect] that notifies when [other] and `this` both say [shouldNotify]. Aspect operator &(DependableAspect other) => Aspect._( - ({prev, next, aspect}) => + ({required prev, required next, required aspect}) => didUpdateWidget(next: next, prev: prev) & other.didUpdateWidget(next: next, prev: prev), (it) => it, @@ -662,7 +666,7 @@ extension InheritableAspectChainable on DependableAspect { /// Returns an [InheritableAspect] that notifies when either [other] or `this` say [shouldNotify]. Aspect operator |(DependableAspect other) => Aspect._( - ({next, prev, aspect}) => + ({required next, required prev, required aspect}) => didUpdateWidget(next: next, prev: prev) | other.didUpdateWidget(next: next, prev: prev), (it) => it, @@ -709,7 +713,7 @@ class NoAspect extends EquatableAspect TransformingAspect, DefaultAspectofContext { @override - final Key key; + final Key? key; /// {@macro InheritableAspect.none} const NoAspect(this.key) : super('NoAspect'); @@ -727,19 +731,19 @@ class NoAspect extends EquatableAspect /// /// {@macro InheritableAspect.of.defaultValue} @override - T of(context, {rebuild = true, T defaultValue}) { + T? of(context, {rebuild = true, T? defaultValue}) { return Inheritable.of(context, aspect: this, rebuild: rebuild) ?.valueFor(this) ?? defaultValue; } @override - InheritableAspect ensureHasKey({Key fallback}) { + InheritableAspect ensureHasKey({fallback}) { return key != null ? this : NoAspect(fallback); } @override - InheritableAspect clone({Key key}) { + InheritableAspect clone({key}) { return NoAspect(key); } } @@ -750,6 +754,7 @@ class _ValueAspect extends EquatableAspect ClonableAspect, DelegatingAspect, TransformingAspect, + DefaultAspectofContext, DelegatingDependableAspect { @override final InheritableAspect delegate; @@ -757,7 +762,7 @@ class _ValueAspect extends EquatableAspect _ValueAspect(this.delegate) : super('ValueAspect of ${delegate.debugLabel}'); @override - DelegatingAspect clone({Key key, InheritableAspect delegate}) { + DelegatingAspect clone({key, delegate}) { return _ValueAspect(cloneDelegate(replacement: delegate, key: key)); } @@ -766,11 +771,8 @@ class _ValueAspect extends EquatableAspect return value; } - /// {@macro InheritableAspect.of} - /// - /// {@macro InheritableAspect.of.defaultValue} @override - T of(context, {rebuild = true, T defaultValue}) { + of(context, {rebuild = true, defaultValue}) { return Inheritable.of(context, aspect: this, rebuild: rebuild) ?.valueFor(this) ?? defaultValue; @@ -795,13 +797,13 @@ class _ListenableAspect extends EquatableAspect : super('ListenableAspect of ${_delegate.debugLabel}'); @override - DelegatingAspect ensureHasKey({Key fallback}) { + DelegatingAspect ensureHasKey({Key? fallback}) { _delegate = ensureDelegateHasKey(null, fallback); return this; } @override - bool didUpdateWidget({prev, next}) { + bool didUpdateWidget({required prev, required next}) { if (super.didUpdateWidget(prev: prev, next: next)) { _value = next.valueFor(this); notifyListeners(); @@ -861,7 +863,7 @@ class _ListenableAspect extends EquatableAspect /// /// ``` @override - ValueListenable of(context, {rebuild = true, T defaultValue}) { + ValueListenable of(context, {rebuild = true, T? defaultValue}) { _value = Inheritable.of( context, aspect: this, @@ -873,7 +875,7 @@ class _ListenableAspect extends EquatableAspect } @override - DelegatingAspect clone({Key key, InheritableAspect delegate}) { + DelegatingAspect clone({key, delegate}) { _delegate = cloneDelegate(key: key, replacement: delegate); return this; @@ -918,6 +920,7 @@ class _ListenableAspect extends EquatableAspect void dispose() { assert((() => _isDisposed = true)()); + _value = null; notifier.dispose(); } @@ -934,9 +937,9 @@ class _ListenableAspect extends EquatableAspect notifier.removeListener(listener); } - T _value; + T? _value; @override - T get value => _value; + T get value => _value as T; } extension ValueAspect on InheritableAspect { @@ -961,9 +964,9 @@ extension ListenableAspect on InheritableAspect { typedef DefaultInheritableAspectOfContext = A Function(BuildContext context); typedef DidUpdateWidget = bool Function({ - Inheritable next, - Inheritable prev, - InheritableAspect aspect, + required Inheritable next, + required Inheritable prev, + required InheritableAspect aspect, }); /// An Aspect that allows updating it's value. @@ -994,10 +997,10 @@ abstract class AspectBatch { Key get key; /// Inheritables satisfying this batch's aspects. - Set> get inheritables; + Set> get inheritables; /// InheritableAspects participating in this batch. - Set> get aspects; + Set> get aspects; /// Apply this batch to [context] void apply(BuildContext context) { @@ -1045,13 +1048,11 @@ typedef InheritableMutation = T Function(Inheritable inheritable); class AspectMutation extends EquatableAspect with ClonableAspect, MutableInheritableAspect { @override - final Key key; + final Key? key; final InheritableMutation mutation; - const AspectMutation(this.mutation, [this.key]) - : assert(mutation != null), - super('AspectMutation'); + const AspectMutation(this.mutation, [this.key]) : super('AspectMutation'); @override T mutate(Inheritable inheritable) { @@ -1063,16 +1064,16 @@ class AspectMutation extends EquatableAspect final inheritable = Inheritable.of(context, aspect: this, rebuild: false); - final ValueChanged Function(T) _defaultTransform = - inheritable is MutableInheritable ? null : (_) => null; + final ValueChanged Function(T)? _defaultTransform = + inheritable is MutableInheritable ? null : (_) => (_) {}; inheritable?.valueFor - ?.call>(this, _defaultTransform) - ?.call(mutate(inheritable)); + .call>(this, _defaultTransform) + .call(mutate(inheritable)); } @override - AspectMutation clone({Key key, InheritableMutation mutation}) { + AspectMutation clone({key, InheritableMutation? mutation}) { if ([key, mutation].whereType().isEmpty) return this; return AspectMutation(mutation ?? this.mutation, key ?? this.key); @@ -1092,31 +1093,33 @@ class Aspect extends EquatableAspect TransformingAspect, PatchableAspect { @override - final Key key; + final Key? key; final A Function(T) mapper; - final AspectPatch _patch; - final DidUpdateWidget _didUpdateWidgetImpl; - final DefaultInheritableAspectOfContext _defaultValue; + final AspectPatch? _patch; + final DidUpdateWidget? _didUpdateWidgetImpl; + final DefaultInheritableAspectOfContext? _defaultValue; /// Create an [InheritableAspect] of [T] that depends on [Inheritable] of [T] /// for any changes of [A] produced by [fn]. - const Aspect(A Function(T) fn, [Key key]) : this._(null, fn, key, null); + const Aspect(A Function(T) fn, [Key? key]) : this._(null, fn, key, null); /// Create an aspect [A] of [T] using [transform], /// which can optionaly be later patched using [patch]. const Aspect.patchable({ - @required AspectPatch patch, - Key key, - A Function(T) transform, + required AspectPatch patch, + required A Function(T) transform, + Key? key, }) : this._(null, transform, key, null, patch); const Aspect._(this._didUpdateWidgetImpl, this.mapper, [this.key, this._defaultValue, this._patch]) - : assert(mapper != null), - super('Aspect'); + : super('Aspect'); - bool _defaultDidUpdateWidgetImpl( - {Inheritable next, Inheritable prev, InheritableAspect aspect}) { + bool _defaultDidUpdateWidgetImpl({ + required Inheritable next, + required Inheritable prev, + required InheritableAspect aspect, + }) { return next.valueFor(aspect, mapper) != prev.valueFor(aspect, mapper); } @@ -1124,7 +1127,7 @@ class Aspect extends EquatableAspect _didUpdateWidgetImpl ?? _defaultDidUpdateWidgetImpl; @override - bool didUpdateWidget({prev, next}) { + bool didUpdateWidget({required prev, required next}) { return didUpdateWidgetImpl(next: next, prev: prev, aspect: this); } @@ -1135,7 +1138,8 @@ class Aspect extends EquatableAspect @override T patch(T value, A next) { - if (_patch != null) return _patch(value, next); + final patch = _patch; + if (patch is AspectPatch) return patch(value, next); throw StateError('This aspect is not Patchable'); } @@ -1144,7 +1148,7 @@ class Aspect extends EquatableAspect /// /// {@macro InheritableAspect.of.defaultValue} @override - A of(context, {rebuild = true, A defaultValue}) { + of(context, {rebuild = true, A? defaultValue}) { final obj = Inheritable.of(context, aspect: this, rebuild: rebuild) ?.valueFor(this, mapper); @@ -1176,14 +1180,14 @@ class Aspect extends EquatableAspect @override Aspect clone({ - Key key, - final A Function(T) mapper, - final AspectPatch patch, - final DidUpdateWidget didUpdateWidget, - final DefaultInheritableAspectOfContext defaultValue, + Key? key, + final A Function(T)? mapper, + final AspectPatch? patch, + final DidUpdateWidget? didUpdateWidget, + final DefaultInheritableAspectOfContext? defaultValue, }) { if ([key, mapper, patch, didUpdateWidget, defaultValue] - .whereType() + .where((o) => o != null) .isEmpty) return this; return Aspect._( @@ -1196,7 +1200,7 @@ class Aspect extends EquatableAspect } @override - InheritableAspect ensureHasKey({Key fallback}) { + InheritableAspect ensureHasKey({fallback}) { return key != null ? this : clone(key: fallback); } } @@ -1204,36 +1208,39 @@ class Aspect extends EquatableAspect // TODO: Extract out TransformingAspect applicable extension methods extension AspectChianingFn on Aspect { /// Use [other] to map the already mapped value by [mapper] for notifications of [T] - Aspect map(RR Function(R) other, [Key key]) { + Aspect map(RR Function(R) other, [Key? key]) { + final defaultValue = _defaultValue; return Aspect._( null, (t) => other(mapper(t)), key ?? this.key, - (_) => _defaultValue != null ? other?.call(_defaultValue?.call(_)) : null, + defaultValue is DefaultInheritableAspectOfContext + ? (_) => other(defaultValue(_)) + : null, ); } /// Allow patching [R] of [T] using [patch] - Aspect withPatch(AspectPatch patch, [Key key]) { + Aspect withPatch(AspectPatch patch, [Key? key]) { return clone(key: key, patch: patch); } /// Add default value to this, if no satisfiable [Inheritable] of [T] can be found - Aspect withDefault(R value, [Key key]) { + Aspect withDefault(R value, [Key? key]) { return clone(key: key, defaultValue: (_) => value); } /// Add default value to this based on provided [BuildContext], if no satisfiable [Inheritable] of [T] can be found. Aspect withDefaultFor(DefaultInheritableAspectOfContext fn, - [Key key]) { + [Key? key]) { return clone(key: key, defaultValue: fn); } /// {@macro InheritableAspect.where} - Aspect where(PredicateAspect predicate, [Key key]) { + Aspect where(PredicateAspect predicate, [Key? key]) { return clone( key: key, - didUpdateWidget: ({next, prev, aspect}) => + didUpdateWidget: ({required next, required prev, required aspect}) => didUpdateWidgetImpl(next: next, prev: prev, aspect: aspect) & predicate( next: next.valueFor(this, mapper), @@ -1250,18 +1257,23 @@ extension AspectChianingFn on Aspect { return mapped; } - return null; + throw StateError( + 'Expected mapped value to be of type $RR but was ${mapper.runtimeType}'); } /// {@macro InheritableAspect.whereType} - Aspect whereType([Key key]) { + Aspect whereType([Key? key]) { + final defaultValue = _defaultValue; return Aspect._( - ({next, prev, aspect}) => + ({required next, required prev, required aspect}) => + // This uses [mapper] and not the updated mapper below didUpdateWidgetImpl(next: next, prev: prev, aspect: aspect) & (next is RR), (t) => _whereType(t, mapper), key ?? this.key, - (_) => _defaultValue != null && R == RR ? _defaultValue(_) as RR : null, + defaultValue is DefaultInheritableAspectOfContext && R == RR + ? (_) => defaultValue(_) as RR + : null, ); } @@ -1272,14 +1284,14 @@ extension AspectChianingFn on Aspect { /// Returns an [InheritableAspect] that notifies when [other] and `this` both say [shouldNotify]. Aspect operator &(DependableAspect other) => clone( - didUpdateWidget: ({next, prev, aspect}) => + didUpdateWidget: ({required next, required prev, required aspect}) => didUpdateWidgetImpl(next: next, prev: prev, aspect: aspect) & other.didUpdateWidget(next: next, prev: prev), ); /// Returns an [InheritableAspect] that notifies when either [other] or `this` say [shouldNotify]. Aspect operator |(DependableAspect other) => clone( - didUpdateWidget: ({next, prev, aspect}) => + didUpdateWidget: ({required next, required prev, required aspect}) => didUpdateWidgetImpl(next: next, prev: prev, aspect: aspect) | other.didUpdateWidget(next: next, prev: prev), ); @@ -1288,7 +1300,7 @@ extension AspectChianingFn on Aspect { typedef AspectWidgetBuilder = Widget Function( BuildContext context, T aspect, - Widget child, + Widget? child, ); /// Convenience widget to get [aspect] as part of the [build] method @@ -1297,13 +1309,13 @@ class AspectBuilder extends StatelessWidget { final TransformingAspect aspect; /// Widget builder that get's [aspect] fed into it - final AspectWidgetBuilder builder; + final AspectWidgetBuilder builder; /// Child widget that doesn't depend on [aspect] - final Widget child; + final Widget? child; /// Default value when [aspect] returns `null` - final A defaultValue; + final A? defaultValue; /// Create a widget that provides [aspect] to it's decedents. /// @@ -1317,18 +1329,22 @@ class AspectBuilder extends StatelessWidget { /// ) /// ``` const AspectBuilder({ - @required this.aspect, - @required this.builder, + required this.aspect, + required this.builder, this.child, this.defaultValue, - Key key, - }) : assert(aspect != null), - assert(builder != null), - super(key: key); + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { - return builder(context, aspect.of(context) ?? defaultValue, child); + final aspect = this.aspect; + return builder( + context, + aspect is DefaultAspectofContext + ? aspect.of(context, defaultValue: defaultValue) + : aspect.of(context) ?? defaultValue, + child); } } @@ -1356,18 +1372,13 @@ class Inheritable extends InheritedWidget { /// /// {@endtemplate} // TODO: if Inheritable is allowed to extend, provide Inheritable.inheriFrom - static Inheritable of( + static Inheritable? of( BuildContext context, { - InheritableAspect aspect, + required InheritableAspect aspect, bool rebuild = true, bool nullOk = true, bool mutable = false, }) { - assert(mutable != null); - if (aspect == null) - throw UnsupportedError( - 'Cannot depend on Inheritable<$T> without specifying an aspect', - ); final result = _findInheritableSupportingAspect(context, aspect, mutable: mutable); @@ -1395,18 +1406,18 @@ class Inheritable extends InheritedWidget { return result.widget; } - static _InheritableElement _findEnclosingInheritableElement( - BuildContext context) { + static _InheritableElement? _findEnclosingInheritableElement( + BuildContext? context) { if (context == null) return null; - _InheritableElement result; + _InheritableElement? result; final inheritable = context.getElementForInheritedWidgetOfExactType>() - as _InheritableElement; + as _InheritableElement?; final mutable = context .getElementForInheritedWidgetOfExactType<_MutableInheritable>() - as _InheritableElement; + as _InheritableElement?; // If we have both implementations available, prefer the one with higher depth if (inheritable != null && mutable != null) { @@ -1427,8 +1438,8 @@ class Inheritable extends InheritedWidget { return result; } - static _InheritableElement _findInheritableSupportingAspect( - BuildContext context, + static _InheritableElement? _findInheritableSupportingAspect( + BuildContext? context, InheritableAspect aspect, { bool mutable = false, }) { @@ -1444,7 +1455,7 @@ class Inheritable extends InheritedWidget { // Go up ancestor, if there is any. /// Copied logic from [InheritedModel._findModels] // TODO: This might not actually be required, investigate, whether flutter devs added this on a fluke. - Element parent; + Element? parent; element.visitAncestorElements((Element ancestor) { parent = ancestor; return false; @@ -1457,15 +1468,16 @@ class Inheritable extends InheritedWidget { ); } - static Set newOverridesSet(Set> other) { + static Set newOverridesSet( + Set>? other) { if (other == null) return {}; - bool hashKeysOnly; + bool? hashKeysOnly; return HashSet( isValidKey: (obj) => obj != null, hashCode: (Object obj) { // after the first time, we don't care for other objects hashKeysOnly ??= obj is _AspectOverrideByKey; - if (hashKeysOnly && obj is InheritableAspect) { + if (hashKeysOnly! && obj is InheritableAspect) { return obj.key.hashCode; } @@ -1487,10 +1499,10 @@ class Inheritable extends InheritedWidget { Set> get overrides => Set.from(_overrides); const Inheritable._({ - T value, - Key key, - Set overrides, - Widget child, + required T value, + Key? key, + Set? overrides, + Widget? child, }) : _value = value, _overrides = overrides ?? const {}, super(key: key, child: child ?? const SizedBox.shrink()); @@ -1499,9 +1511,9 @@ class Inheritable extends InheritedWidget { /// /// Optionally specify [onRequestUpdate] const Inheritable({ - T value, - Key key, - Widget child, + required T value, + Key? key, + Widget? child, }) : this._( value: value, key: key, @@ -1547,27 +1559,27 @@ class Inheritable extends InheritedWidget { /// [strict] is only used in debug mode. static Widget supply({ - List> inheritables, - Widget child, + required List> inheritables, + Widget? child, bool strict = true, }) { - Widget result = child; - Map> _; + Widget? result = child; + Map>? _; // TODO: Add further optimizations to Inheritable.supply for (var inheritable in inheritables.reversed) { assert(!strict || (() { - _ ??= >{}; + _ ??= >{}; final type = inheritable.runtimeType; final key = inheritable.key; - final keys = _[type] ??= HashSet( + final keys = _?[type] ??= HashSet( equals: (a, b) => a == b, hashCode: (a) => a.hashCode, isValidKey: (_) => true, // null is also valid ); - if (keys.contains(key)) { + if (keys!.contains(key)) { throw StateError( 'Found duplicate inheritable [$type] with key [$key] in $keys. ' 'This is essentially overriding access to the original inheritable. ' @@ -1589,7 +1601,7 @@ class Inheritable extends InheritedWidget { return true; })()); - return result; + return result!; } /// Create an [Inheritable] that overrides [T] with given [value] for the widget sub-tree. @@ -1618,11 +1630,11 @@ class Inheritable extends InheritedWidget { /// ``` static Inheritable override({ - SubType value, - Key key, - Set> overrides, - ValueChanged onMutate, - Widget child, + required SubType value, + Key? key, + Set>? overrides, + ValueChanged? onMutate, + Widget? child, bool strict = true, }) { assert( @@ -1658,7 +1670,7 @@ class Inheritable extends InheritedWidget { } /// Convenience method when [Inheritable.mutable] should ignore all mutations - static void ignoreMutation(Object obj) {} + static void ignoreMutation(Object? obj) {} /// Mutable variant of [Inheritable], users are to provide [onMutate] to allow /// value to change. @@ -1666,8 +1678,8 @@ class Inheritable extends InheritedWidget { /// However dependents have no say whether a supplied value should be /// updated or not. const factory Inheritable.mutable({ - @required ValueChanged onMutate, - T value, + required ValueChanged onMutate, + required T value, Key key, Widget child, }) = _MutableInheritable; @@ -1677,10 +1689,10 @@ class Inheritable extends InheritedWidget { /// /// Returns `this` if all parameters are null. Inheritable copyWith({ - Key key, - T value, - Set> overrides, - Widget child, + Key? key, + T? value, + Set>? overrides, + Widget? child, }) { if ([key, value, overrides, child].whereType().isEmpty) return this; @@ -1698,11 +1710,11 @@ class Inheritable extends InheritedWidget { /// provides the overridden value if there is one, or [_value] by [transform] /// /// Optionally provide [transform]. By default [_value] is simply casted - A valueFor(InheritableAspect aspect, [A Function(T) transform]) { + A valueFor(InheritableAspect aspect, [A Function(T)? transform]) { assert(isSupportedAspect(aspect)); final maybeOverride = _overrides.lookup(aspect); - if (maybeOverride != null && maybeOverride is AspectOverride) { + if (maybeOverride is AspectOverride) { if (maybeOverride.override is A || maybeOverride.override == null) return maybeOverride.override as A; else @@ -1712,8 +1724,6 @@ class Inheritable extends InheritedWidget { ); } - if (_value == null) return null; - if (aspect is TransformingAspect) { transform ??= aspect.transform; } @@ -1771,14 +1781,14 @@ mixin MutableInheritable on Inheritable { /// ```dart /// MutableInheritable.of(context).value = User('new', 'user'); /// ``` - static MutableInheritable of(BuildContext context, - [InheritableAspect aspect]) { + static MutableInheritable? of(BuildContext context, + {required InheritableAspect aspect}) { /// We don't use [Inheritable._findEnclosingInheritableElement(context)] /// here because [Inheritable] is not accepted in this case return Inheritable._findInheritableSupportingAspect(context, aspect, mutable: true) - ?.widget as MutableInheritable; + ?.widget as MutableInheritable?; } } @@ -1787,22 +1797,18 @@ class _MutableInheritable extends Inheritable final ValueChanged onMutate; const _MutableInheritable._({ - @required this.onMutate, - T value, - Key key, - Set overrides, - Widget child, - }) : assert( - onMutate != null, - 'Prefer creating an Inheritable if all changes will be rejected', - ), - super._(value: value, key: key, overrides: overrides, child: child); + required this.onMutate, + required T value, + Key? key, + Set? overrides, + Widget? child, + }) : super._(value: value, key: key, overrides: overrides, child: child); const _MutableInheritable({ - ValueChanged onMutate, - T value, - Key key, - Widget child, + required ValueChanged onMutate, + required T value, + Key? key, + Widget? child, }) : this._( onMutate: onMutate, value: value, @@ -1811,7 +1817,7 @@ class _MutableInheritable extends Inheritable ); @override - A valueFor(InheritableAspect aspect, [A Function(T p1) transform]) { + A valueFor(InheritableAspect aspect, [A Function(T p1)? transform]) { if (aspect is MutableInheritableAspect) { assert([] is List>); return super.valueFor(aspect, transform ?? (_) => onMutate as A); @@ -1822,7 +1828,7 @@ class _MutableInheritable extends Inheritable @override void mutateBy(MutableInheritableAspect aspect) { - valueFor>(aspect)?.call(aspect.mutate(this)); + valueFor?>(aspect)?.call(aspect.mutate(this)); } /// Create a new [Inheritable] of [T] with it's properties changed with the @@ -1831,11 +1837,11 @@ class _MutableInheritable extends Inheritable /// Returns `this` if all parameters are null. @override Inheritable copyWith({ - Key key, - T value, - Set> overrides, - Widget child, - ValueChanged onMutate, + Key? key, + T? value, + Set>? overrides, + Widget? child, + ValueChanged? onMutate, }) { if ([key, value, onMutate, child, overrides].whereType().isEmpty) return this; @@ -1875,15 +1881,15 @@ class _InheritableElement extends InheritedElement { } bool removeAspect(Element dependent, InheritableAspect aspect) { - return removeKey(dependent, aspect?.key); + return removeKey(dependent, aspect.key!); } bool removeAllAspects(Element dependent, - [Set> aspects]) { + [Set>? aspects]) { return removeAllKeys( dependent, // if it's null, remove all aspects - aspects?.map((a) => a.key)?.toSet(), + aspects?.map((a) => a.key).toSet(), ); } @@ -1898,8 +1904,8 @@ class _InheritableElement extends InheritedElement { return removed; } - bool removeAllKeys(Element dependent, [Set keys]) { - if (keys != null && keys.isEmpty) return false; + bool removeAllKeys(Element dependent, [Set? keys]) { + if (keys != null && keys.whereType().isEmpty) return false; final dependencies = getDependencies(dependent); @@ -1912,32 +1918,32 @@ class _InheritableElement extends InheritedElement { return true; } - keys = Set.of(keys); + final _keys = Set.of(keys.whereType()); - dependencies.removeWhere((k, _) => keys.remove(k)); + dependencies.removeWhere((k, _) => _keys.remove(k)); - return keys.isEmpty; + return _keys.isEmpty; } @override - Map> getDependencies(Element dependent) { - return super.getDependencies(dependent) as Map>; + Map>? getDependencies(Element dependent) { + return super.getDependencies(dependent) as Map>?; } @override - void updateDependencies(Element dependent, Object aspect) { + void updateDependencies(Element dependent, Object? aspect) { final dependencies = getDependencies(dependent) ?? _newMap(); if (aspect is DependableAspect) { // This allow replacing aspects by using same key - dependencies[aspect.key] = aspect; + dependencies[aspect.key!] = aspect; setDependencies(dependent, dependencies); } else { /// [dependent] is requesting unconditional notifications. Or the aspect /// can't be used for depending on `this` /// Disallow that. - InformationCollector collector; + InformationCollector? collector; assert(() { collector = () sync* { yield DiagnosticsProperty( @@ -2014,7 +2020,7 @@ class _BuildContextAspect { /// {@macro InheritableAspect.of.defaultValue} /// /// {@endtemplate} - A call(ExtractAspect extract, {A defaultValue, Key key}) { + A? call(ExtractAspect extract, {A? defaultValue, Key? key}) { return _dispose((context) { return Aspect(extract, key).of( context, @@ -2035,7 +2041,7 @@ class _BuildContextAspect { } /// {@macro BuildContextAspect.call} - A get(ExtractAspect extract, {A defaultValue, Key key}) { + A? get(ExtractAspect extract, {A? defaultValue, Key? key}) { return call(extract, defaultValue: defaultValue, key: key); } @@ -2044,7 +2050,7 @@ class _BuildContextAspect { /// /// You can also specify [defaultValue] when there is no enclosing /// [Inheritable.model] of [T] - T where(PredicateAspect predicate, {T defaultValue, Key key}) { + T? where(PredicateAspect predicate, {T? defaultValue, Key? key}) { return _dispose((context) { return NoAspect(key) .where(predicate) @@ -2078,9 +2084,9 @@ class _BuildContextAspect { /// `false`: Removed some of the [aspects] or none of them. // TODO: write test for context.removeAll @visibleForTesting - bool removeAll([Set> aspects]) { + bool removeAll([Set>? aspects]) { return _dispose((context) { - final _aspects = List.of(aspects); + final _aspects = List.of(aspects ?? >{}); for (var i = 0; i < _aspects.length; i++) { final element = Inheritable._findInheritableSupportingAspect( context, @@ -2144,7 +2150,7 @@ class _BuildContextAspect { 'Prefer using [removeAll] instead.', ) // TODO: write test for context.removeAllKeys - bool removeAllKeys([Set keys]) { + bool removeAllKeys([Set? keys]) { assert( T != dynamic, 'Specify the exact type of Inheritable, dynamic is probably not what you want', @@ -2169,7 +2175,7 @@ class _BuildContextAspect { /// Inheritable.of(context, aspect: const NoAspect()) /// /// {@endtemplate} - T none({T defaultValue, Key key}) { + T? none({T? defaultValue, Key? key}) { return _dispose((context) { return InheritableAspect.none(key).of( context, diff --git a/lib/src/_wip.dart b/lib/src/_wip.dart index d2f8a43..5fdbbac 100644 --- a/lib/src/_wip.dart +++ b/lib/src/_wip.dart @@ -43,14 +43,14 @@ class AsyncAspectBuilder extends StatelessWidget { final AsyncWidgetBuilder builder; const AsyncAspectBuilder({ - Key key, - this.builder, - this.aspect, + required this.builder, + required this.aspect, + Key? key, }) : super(key: key); @override Widget build(BuildContext context) { - return builder(context, aspect.of(context)); + return builder(context, aspect.of(context)!); } } @@ -95,7 +95,7 @@ mixin LazyAspect on StatefulInheritableAspect { // } @visibleForTesting mixin MemoizedLazyAspect on LazyAspect { - T _memoized; + T? _memoized; @override get value => _memoized ??= super.value; @@ -136,24 +136,24 @@ mixin AutoDisposeAspectResources on Widget { } @visibleForTesting mixin AutoDisposeAspectResourcesElement on Element { - Map, Object> _aspectsInUse; + late Map, Object> _aspectsInUse; void addResourceForAspect(DisposableAspect aspect, R resource) => - _aspectsInUse[aspect] = resource; + _aspectsInUse[aspect] = resource as Object; R getResourceForAspect(DisposableAspect aspect) => _aspectsInUse[aspect] as R; @override - void mount(Element parent, newSlot) { + void mount(Element? parent, newSlot) { _aspectsInUse = {}; super.mount(parent, newSlot); } @override void unmount() { - _aspectsInUse?.forEach((a, _) => a.dispose(this)); - _aspectsInUse = null; + _aspectsInUse.forEach((a, _) => a.dispose(this)); + _aspectsInUse.clear(); super.unmount(); } @@ -176,7 +176,7 @@ class AutoDisposeAspectResourcesStatefulElement extends StatefulElement @visibleForTesting class AspectListenableBuilder extends StatelessWidget with AutoDisposeAspectResources { - const AspectListenableBuilder({Key key}) : super(key: key); + const AspectListenableBuilder({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index 5668b33..b62b636 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,49 +7,49 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.13" + version: "1.15.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -66,21 +66,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.8" + version: "0.12.10" meta: dependency: "direct main" description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" sky_engine: dependency: transitive description: flutter @@ -92,56 +92,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.5" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.17" + version: "0.2.19" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0" sdks: - dart: ">=2.9.0 <3.0.0" - flutter: ">=1.20.4 <1.26.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index eefbb00..37adefd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,8 +6,8 @@ issue_tracker: https://github.com/devkabiir/inheritable/issues version: 1.0.0-dev.1 environment: - sdk: '>=2.9.0 <3.0.0' - flutter: '>=1.20.4 <1.26.0' + sdk: '>=2.12.0 <3.0.0' + flutter: '>=2.0.0' dependencies: flutter: diff --git a/test/inheritable_test.dart b/test/inheritable_test.dart index 46483e7..074e2fc 100644 --- a/test/inheritable_test.dart +++ b/test/inheritable_test.dart @@ -5,15 +5,15 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; class User { - String fname; - String lname; + late String fname; + late String lname; - static String firstName(User user) => user.fname; - static String lastName(User user) => user.lname; - static String fullName(User user) => '${user.fname} ${user.lname}'; + static String? firstName(User? user) => user?.fname; + static String? lastName(User? user) => user?.lname; + static String? fullName(User? user) => '${user?.fname} ${user?.lname}'; - static List bothField(User user) { - return user.both; + static List? bothField(User? user) { + return user?.both; } static String displayW(String key, A aspect, int buildCount) => @@ -57,7 +57,7 @@ extension on WidgetTester { /// This has a similar effect to routing to a different page Future disposeWidgets() async { await pumpWidget(const SizedBox()); - return pumpAndSettle(); + return pumpAndSettle().then((_) => null); } } @@ -72,11 +72,11 @@ class Variant extends TestVariant { return value.toString(); } - T _currentValue; - T get currentValue => _currentValue; + T? _currentValue; + T get currentValue => _currentValue!; @override - Future setUp(T value) async { + Future setUp(T value) async { _currentValue = value; return null; } @@ -87,14 +87,14 @@ class Variant extends TestVariant { } } -Future main([List args]) async { +Future main([List? args]) async { testWidgets('Throws for unsatisfied dependency', (tester) async { final defaultObj = Object(); var dependency = defaultObj; await tester.pumpStatefulWidget( (context, setState) => dependency = Inheritable.of(context, - aspect: const NoAspect(null), nullOk: false), + aspect: const NoAspect(null), nullOk: false)!, ); expect(tester.takeException(), isA()); @@ -104,14 +104,15 @@ Future main([List args]) async { testWidgets('Returns null for unsatisfied dependency [nullOk]', (tester) async { final defaultObj = Object(); - var dependency = defaultObj; + Object? dependency = defaultObj; await tester.pumpStatefulWidget( (context, setState) { dependency = Inheritable.of(context, aspect: const NoAspect(null), nullOk: true); return Text( - (dependency as Inheritable)?.valueFor(null) ?? 'nothing'); + (dependency as Inheritable?)?.toString() ?? 'nothing', + ); }, ); @@ -122,11 +123,11 @@ Future main([List args]) async { testWidgets('Throws for unsatisfied mutable dependency', (tester) async { final defaultObj = Object(); - var dependency = defaultObj; + Object? dependency = defaultObj; await tester.pumpStatefulWidget( (context, setState) => dependency = Inheritable.of(context, - aspect: const NoAspect(null), nullOk: false, mutable: true), + aspect: const NoAspect(null), nullOk: false, mutable: true)!, ); expect(tester.takeException(), isA()); @@ -136,14 +137,14 @@ Future main([List args]) async { testWidgets('Returns null for unsatisfied dependency [nullOk]', (tester) async { final defaultObj = Object(); - var dependency = defaultObj; + Object? dependency = defaultObj; await tester.pumpStatefulWidget( (context, setState) { dependency = Inheritable.of(context, aspect: const NoAspect(null), nullOk: true, mutable: true); return Text( - (dependency as Inheritable)?.valueFor(null) ?? 'nothing'); + (dependency as Inheritable?)?.toString() ?? 'nothing'); }, ); @@ -164,7 +165,7 @@ Future main([List args]) async { builder: (context) { final fullName = context.aspect((User u) => '${u.fname} ${u.lname}'); - return Text(fullName); + return Text(fullName!); }, ), ), @@ -195,13 +196,13 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable( + return Inheritable( key: const Key('test-key'), value: user, child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -261,13 +262,13 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable( + return Inheritable( key: const Key('test-key'), value: user, child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -320,7 +321,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -348,43 +349,6 @@ Future main([List args]) async { expect(User.stateW('no-aspect', '$user', 2), findsOneWidget); }); - testWidgets('Disallows dependents without any aspect', (tester) async { - var user = User() - ..fname = 'first' - ..lname = 'last'; - - const nullAspect = _NullAspect(key: ValueKey('null-aspect')); - - await tester.pumpStatefulWidget( - (context, setState) { - return Inheritable( - key: const Key('test-key'), - value: user, - child: Column( - key: const Key('column'), - children: [ - FlatButton( - key: const Key('button'), - onPressed: () { - setState(() { - user = User() - ..fname = 'first' - ..lname = 'last2'; - }); - }, - child: const Text('change-state'), - ), - const Flexible(child: nullAspect), - ], - ), - ); - }, - ); - - expect(tester.takeException(), isUnsupportedError); - expect(User.stateW('null-aspect', '$user', 1), findsNothing); - }); - testWidgets('Notifies some-aspect dependents', (tester) async { var user = User() ..fname = 'first' @@ -406,7 +370,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -460,7 +424,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -499,7 +463,7 @@ Future main([List args]) async { { Aspect((User u) => u.fname, const Key('user-fname')), Aspect((User u) => u.lname, const Key('user-lname')) - .where(({next, prev}) { + .where(({required next, required prev}) { return next != 'last2'; }), }, @@ -514,7 +478,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -553,7 +517,7 @@ Future main([List args]) async { { Aspect((User u) => u.fname, const Key('user-fname')), Aspect((User u) => u.lname, const Key('user-lname')) - .where(({next, prev}) => next == 'last2'), + .where(({required next, required prev}) => next == 'last2'), }, key: const ValueKey('some-aspect'), ); @@ -566,7 +530,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -617,7 +581,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -667,7 +631,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -721,7 +685,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -760,7 +724,7 @@ Future main([List args]) async { { Aspect((User u) => u.fname, const Key('user-fname')), Aspect((User u) => u.lname, const Key('user-lname')) - .where(({next, prev}) { + .where(({required next, required prev}) { return next != 'last2'; }), }, @@ -775,7 +739,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -815,7 +779,7 @@ Future main([List args]) async { { Aspect((User u) => u.fname, const Key('user-fname')), Aspect((User u) => u.lname, const Key('user-lname')) - .where(({next, prev}) => next == 'last2'), + .where(({required next, required prev}) => next == 'last2'), }, key: const ValueKey('some-chained-aspect'), ); @@ -828,7 +792,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -884,7 +848,7 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable(value: user), + Inheritable(value: user), Inheritable(value: user.lname), Inheritable(value: User.fullName(user).hashCode), ], @@ -932,8 +896,8 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable(value: user), - Inheritable(value: user), + Inheritable(value: user), + Inheritable(value: user), Inheritable(value: user.lname), Inheritable(value: User.fullName(user).hashCode), ], @@ -977,19 +941,19 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable( + Inheritable( key: const Key('key1'), value: User() ..fname = 'will be' ..lname = 'overridden', ), - Inheritable( + Inheritable( key: const Key('key2'), value: User() ..fname = 'will also be' ..lname = 'overridden', ), - Inheritable(key: const Key('key3'), value: user), + Inheritable(key: const Key('key3'), value: user), ], child: Column( key: const Key('column'), @@ -1035,19 +999,19 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable( + Inheritable( key: const Key('key1'), value: User() ..fname = 'will be' ..lname = 'overridden', ), - Inheritable( + Inheritable( key: const Key('key1'), value: User() ..fname = 'will also be' ..lname = 'overridden', ), - Inheritable(key: const Key('key3'), value: user), + Inheritable(key: const Key('key3'), value: user), ], child: Column( key: const Key('column'), @@ -1089,19 +1053,19 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable( + Inheritable( key: const Key('key1'), value: User() ..fname = 'will be' ..lname = 'overridden', ), - Inheritable( + Inheritable( key: const Key('key2'), value: User() ..fname = 'will also be' ..lname = 'overridden', ), - Inheritable(value: user), + Inheritable(value: user), ], child: Column( key: const Key('column'), @@ -1176,9 +1140,61 @@ Future main([List args]) async { }); testWidgets( - '[strict:false] Notifies dependents for multiple Inheritables (unique-by-types)', + '[strict:false] Notifies dependents for multiple Inheritables (unique-by-keys)', (tester) async { - expect(true, isTrue); + final user = User() + ..fname = 'first' + ..lname = 'last'; + + const firstNameW = _ExtractMutableAspectW( + User.firstName, + key: ValueKey('first-name'), + ); + const lastNameW = _ExtractMutableAspectW( + User.lastName, + key: ValueKey('last-name'), + ); + const fullNameW = _ExtractMutableAspectW( + User.fullName, + key: ValueKey('full-name'), + ); + + await tester.pumpStatefulWidget( + (context, setState) { + return Inheritable.supply( + strict: false, + inheritables: [ + Inheritable( + key: const Key('key1'), + value: User() + ..fname = 'will be' + ..lname = 'overridden', + ), + Inheritable( + key: const Key('key2'), + value: User() + ..fname = 'will also be' + ..lname = 'overridden', + ), + Inheritable(key: const Key('key3'), value: user), + ], + child: Column( + key: const Key('column'), + children: const [ + Flexible(child: firstNameW), + Flexible(child: lastNameW), + Flexible(child: fullNameW), + ], + ), + ); + }, + ); + + expect(tester.takeException(), isNull); + + expect(User.stateW('first-name', 'first', 1), findsOneWidget); + expect(User.stateW('last-name', 'last', 1), findsOneWidget); + expect(User.stateW('full-name', 'first last', 1), findsOneWidget); }); testWidgets( @@ -1206,19 +1222,19 @@ Future main([List args]) async { return Inheritable.supply( strict: true, inheritables: [ - Inheritable( + Inheritable( key: const Key('key1'), value: User() ..fname = 'this will be first' ..lname = 'not used', ), - Inheritable( + Inheritable( key: const Key('key2'), value: User() ..fname = 'not used' ..lname = 'this will be last', ), - Inheritable(key: const Key('key3'), value: user), + Inheritable(key: const Key('key3'), value: user), ], child: Column( key: const Key('column'), @@ -1241,22 +1257,163 @@ Future main([List args]) async { }); testWidgets( - '[strict:false] Notifies dependents for multiple Inheritables (unique-by-keys)', + '[strict:false] Notifies dependents for multiple Inheritables (unique-by-types)', (tester) async { - expect(true, isTrue); + final user = User() + ..fname = 'first' + ..lname = 'last'; + + const firstNameW = _ExtractMutableAspectW( + User.firstName, + key: ValueKey('first-name'), + ); + final lastNameW = _ExtractMutableAspectW( + (String lname) => lname, + key: const ValueKey('last-name'), + ); + final fullNameW = _ExtractMutableAspectW( + (int fullName) => fullName, + key: const ValueKey('full-name'), + ); + + await tester.pumpStatefulWidget( + (context, setState) { + return Inheritable.supply( + strict: false, + inheritables: [ + Inheritable(value: user), + Inheritable(value: user.lname), + Inheritable(value: User.fullName(user).hashCode), + ], + child: Column( + key: const Key('column'), + children: [ + const Flexible(child: firstNameW), + Flexible(child: lastNameW), + Flexible(child: fullNameW), + ], + ), + ); + }, + ); + + expect(tester.takeException(), isNull); + expect(User.stateW('first-name', 'first', 1), findsOneWidget); + expect(User.stateW('last-name', 'last', 1), findsOneWidget); + expect( + User.stateW('full-name', 'first last'.hashCode, 1), findsOneWidget); }); testWidgets( 'Can supply [strict:true] multiple Inheritable.mutable (unique-by-types)', (tester) async { - expect(true, isTrue); - }, skip: true); + final user = User() + ..fname = 'first' + ..lname = 'last'; + + const firstNameW = _ExtractMutableAspectW( + User.firstName, + key: ValueKey('first-name'), + ); + final lastNameW = _ExtractMutableAspectW( + (String lname) => lname, + key: const ValueKey('last-name'), + ); + final fullNameW = _ExtractMutableAspectW( + (int fullName) => fullName, + key: const ValueKey('full-name'), + ); + + await tester.pumpStatefulWidget( + (context, setState) { + return Inheritable.supply( + strict: true, + inheritables: [ + Inheritable.mutable(onMutate: (_) {}, value: user), + Inheritable.mutable(onMutate: (_) {}, value: user.lname), + Inheritable.mutable( + onMutate: (_) {}, + value: User.fullName(user).hashCode, + ), + ], + child: Column( + key: const Key('column'), + children: [ + const Flexible(child: firstNameW), + Flexible(child: lastNameW), + Flexible(child: fullNameW), + ], + ), + ); + }, + ); + + expect(tester.takeException(), isNull); + expect(User.stateW('first-name', 'first', 1), findsOneWidget); + expect(User.stateW('last-name', 'last', 1), findsOneWidget); + expect( + User.stateW('full-name', 'first last'.hashCode, 1), findsOneWidget); + }); testWidgets( 'Can supply [strict:true] multiple Inheritable.mutable (unique-by-keys)', (tester) async { - expect(true, isTrue); - }, skip: true); + final user = User() + ..fname = 'first' + ..lname = 'last'; + + const firstNameW = _ExtractMutableAspectW( + User.firstName, + key: ValueKey('first-name'), + ); + const lastNameW = _ExtractMutableAspectW( + User.lastName, + key: ValueKey('last-name'), + ); + const fullNameW = _ExtractMutableAspectW( + User.fullName, + key: ValueKey('full-name'), + ); + + await tester.pumpStatefulWidget( + (context, setState) { + return Inheritable.supply( + strict: true, + inheritables: [ + Inheritable.mutable( + key: const Key('key1'), + onMutate: (_) {}, + value: User() + ..fname = 'will be' + ..lname = 'overridden', + ), + Inheritable.mutable( + key: const Key('key2'), + onMutate: (_) {}, + value: User() + ..fname = 'will also be' + ..lname = 'overridden', + ), + Inheritable(key: const Key('key3'), value: user), + ], + child: Column( + key: const Key('column'), + children: const [ + Flexible(child: firstNameW), + Flexible(child: lastNameW), + Flexible(child: fullNameW), + ], + ), + ); + }, + ); + + expect(tester.takeException(), isNull); + + expect(User.stateW('first-name', 'first', 1), findsOneWidget); + expect(User.stateW('last-name', 'last', 1), findsOneWidget); + expect(User.stateW('full-name', 'first last', 1), findsOneWidget); + }); }); testWidgets( @@ -1281,7 +1438,7 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.mutable( + return Inheritable.mutable( key: const Key('test-key'), value: user, onMutate: Inheritable.ignoreMutation, @@ -1289,7 +1446,7 @@ Future main([List args]) async { key: const Key('column'), children: [ Builder( - builder: (context) => FlatButton( + builder: (context) => TextButton( key: const Key('button'), onPressed: () { context.aspect.update( @@ -1331,7 +1488,7 @@ Future main([List args]) async { testWidgets( 'Notifies parent for mutable value change [parent-accepts-change]', (tester) async { - var user = User() + User? user = User() ..fname = 'first' ..lname = 'last'; @@ -1350,7 +1507,7 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.mutable( + return Inheritable.mutable( key: const Key('test-key'), value: user, onMutate: (next) { @@ -1362,10 +1519,10 @@ Future main([List args]) async { key: const Key('column'), children: [ Builder( - builder: (context) => FlatButton( + builder: (context) => TextButton( key: const Key('button'), onPressed: () { - context.aspect.update( + context.aspect.update( User() ..fname = 'first' ..lname = 'last2', @@ -1437,7 +1594,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -1480,7 +1637,7 @@ Future main([List args]) async { notifyCount += 1; } - final userListener = Aspect((User u) => u.lname, const Key('user-lname')) + final userListener = const Aspect(User.lastName, Key('user-lname')) .listenable ..addListener(didNotify); @@ -1493,13 +1650,13 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable( + return Inheritable( key: const Key('test-key'), value: user, child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -1515,7 +1672,7 @@ Future main([List args]) async { child: Builder( key: const Key('builder'), builder: (context) { - return ValueListenableBuilder( + return ValueListenableBuilder( valueListenable: userListener, builder: (context, user, child) => Text('ValueListenableBuilder: ${user?.lname}'), @@ -1555,13 +1712,13 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable( + return Inheritable( key: const Key('test-key'), value: user, child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -1613,7 +1770,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -1668,7 +1825,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -1724,7 +1881,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button-1'), onPressed: () { setState(() { @@ -1735,7 +1892,7 @@ Future main([List args]) async { }, child: const Text('change-state-1'), ), - FlatButton( + TextButton( key: const Key('button-2'), onPressed: () { setState(() { @@ -1793,7 +1950,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button-1'), onPressed: () { setState(() { @@ -1804,7 +1961,7 @@ Future main([List args]) async { }, child: const Text('change-state-1'), ), - FlatButton( + TextButton( key: const Key('button-2'), onPressed: () { setState(() { @@ -1815,7 +1972,7 @@ Future main([List args]) async { }, child: const Text('change-state-2'), ), - FlatButton( + TextButton( key: const Key('button-3'), onPressed: () { setState(() { @@ -1884,7 +2041,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button-1'), onPressed: () { setState(() { @@ -1895,7 +2052,7 @@ Future main([List args]) async { }, child: const Text('change-state-1'), ), - FlatButton( + TextButton( key: const Key('button-2'), onPressed: () { setState(() { @@ -1906,7 +2063,7 @@ Future main([List args]) async { }, child: const Text('change-state-2'), ), - FlatButton( + TextButton( key: const Key('button-3'), onPressed: () { setState(() { @@ -1973,7 +2130,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button-1'), onPressed: () { setState(() { @@ -1984,7 +2141,7 @@ Future main([List args]) async { }, child: const Text('change-state-1'), ), - FlatButton( + TextButton( key: const Key('button-2'), onPressed: () { setState(() { @@ -1995,7 +2152,7 @@ Future main([List args]) async { }, child: const Text('change-state-2'), ), - FlatButton( + TextButton( key: const Key('button-3'), onPressed: () { setState(() { @@ -2006,7 +2163,7 @@ Future main([List args]) async { }, child: const Text('change-state-3'), ), - FlatButton( + TextButton( key: const Key('button-4'), onPressed: () { setState(() { @@ -2080,7 +2237,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button-1'), onPressed: () { setState(() { @@ -2091,7 +2248,7 @@ Future main([List args]) async { }, child: const Text('change-state-1'), ), - FlatButton( + TextButton( key: const Key('button-2'), onPressed: () { setState(() { @@ -2102,7 +2259,7 @@ Future main([List args]) async { }, child: const Text('change-state-2'), ), - FlatButton( + TextButton( key: const Key('button-3'), onPressed: () { setState(() { @@ -2186,7 +2343,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -2253,7 +2410,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -2322,7 +2479,7 @@ Future main([List args]) async { child: Column( key: const Key('column'), children: [ - FlatButton( + TextButton( key: const Key('button'), onPressed: () { setState(() { @@ -2442,7 +2599,7 @@ Future main([List args]) async { value: user, overrides: { AspectOverride, User>.key( - overriddenAspect.key, + overriddenAspect.key!, (u) => setState(() => overriddenOnMutate .add('call ${overriddenOnMutate.length + 1}')), mutation: true, @@ -2501,7 +2658,7 @@ Future main([List args]) async { value: user, overrides: { AspectOverride, User>.key( - overriddenAspect.key, + overriddenAspect.key!, (u) => setState(() => overriddenOnMutate .add('call ${overriddenOnMutate.length + 1}')), mutation: false, @@ -2536,17 +2693,17 @@ Future main([List args]) async { 'Throws when provided value for aspect is not of expected type (aspect equality by key)', (tester) async { final overriddenAspectW = _OverridenAspectW( - Aspect((User u) => u.lname, const Key('user-lname')), + Aspect((User? u) => u?.lname, const Key('user-lname')), key: const ValueKey('overridden-aspect'), ); await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( key: const Key('test-key'), value: null, overrides: { - const AspectOverride.key( + const AspectOverride.key( Key('user-lname'), 123, ) @@ -2569,7 +2726,7 @@ Future main([List args]) async { 'Throws when provided value for aspect is not of expected type (aspect equality)', (tester) async { final overriddenAspect = - Aspect((User u) => u.lname, const Key('user-lname')); + Aspect((User? u) => u?.lname, const Key('user-lname')); final overriddenAspectW = _OverridenAspectW( overriddenAspect, key: const ValueKey('overridden-aspect'), @@ -2577,10 +2734,10 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( key: const Key('test-key'), value: null, - overrides: {AspectOverride(overriddenAspect, 123)}, + overrides: {AspectOverride(overriddenAspect, 123)}, strict: false, child: Column( key: const Key('column'), @@ -2623,7 +2780,7 @@ Future main([List args]) async { value: user, overrides: { AspectOverride, User>.key( - overriddenAspect.key, + overriddenAspect.key!, (u) => setState(() => overriddenOnMutate .add('call ${overriddenOnMutate.length + 1}')), mutation: true, @@ -2678,7 +2835,7 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Text(aspect.of(context)); + return Text(aspect.of(context)!); }, ); @@ -2693,7 +2850,7 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Text(aspect.of(context)); + return Text(aspect.of(context)!); }, ); @@ -2706,7 +2863,7 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Text(aspect.of(context, defaultValue: 'use-time')); + return Text(aspect.of(context, defaultValue: 'use-time')!); }, ); @@ -2719,8 +2876,9 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Text( - aspect.map((fname) => fname.hashCode.toString()).of(context)); + return Text(aspect + .map((fname) => fname.hashCode.toString()) + .of(context)!); }, ); @@ -2733,13 +2891,14 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( strict: false, + value: null, overrides: { aspect > 'overridden-value', }, child: Builder( - builder: (context) => Text(aspect.of(context)), + builder: (context) => Text(aspect.of(context)!), ), ); }, @@ -2755,14 +2914,15 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( strict: false, + value: null, overrides: { aspect > 'overridden-value', }, child: Builder( - builder: (context) => - Text(aspect.map((s) => s.hashCode.toString()).of(context)), + builder: (context) => Text( + aspect.map((s) => s.hashCode.toString()).of(context)!), ), ); }, @@ -2780,13 +2940,14 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( strict: false, + value: null, overrides: { aspect > 'overridden-value', }, child: Builder( - builder: (context) => Text(aspect.of(context)), + builder: (context) => Text(aspect.of(context)!), ), ); }, @@ -2803,14 +2964,16 @@ Future main([List args]) async { await tester.pumpStatefulWidget( (context, setState) { - return Inheritable.override( + return Inheritable.override( strict: false, + value: null, overrides: { aspect > 'overridden-value', }, child: Builder( - builder: (context) => - Text(aspect.map((s) => s.hashCode.toString()).of(context)), + builder: (context) => Text(aspect.map((s) { + return s.hashCode.toString(); + }).of(context)!), ), ); }, @@ -2822,15 +2985,10 @@ Future main([List args]) async { expect(find.text('first-name'), findsNothing); expect(find.text('overridden-value'), findsOneWidget); }); - - testWidgets('Throws on missing Inheritable.mutable onMutate', (tester) async { - expect( - () => Inheritable.mutable(onMutate: null), throwsA(isAssertionError)); - }); } class _InlineListenableAspect extends StatefulWidget { - const _InlineListenableAspect({Key key}) : super(key: key); + const _InlineListenableAspect({Key? key}) : super(key: key); @override _InlineListenableAspectState createState() => _InlineListenableAspectState(); } @@ -2849,18 +3007,22 @@ class _InlineListenableAspectState extends State<_InlineListenableAspect> { Widget build(BuildContext context) { _buildCount += 1; - return ValueListenableBuilder( + return ValueListenableBuilder( valueListenable: - const Aspect(User.lastName, Key('user-lname')).listenable.of(context), - builder: (context, user, child) => Text( - User.displayW(key.value, user.lname, _buildCount), - ), + const Aspect(User.lastName, Key('user-lname')) + .listenable + .of(context), + builder: (context, user, child) { + return Text( + User.displayW(key.value, user?.lname, _buildCount), + ); + }, ); } } class _NoAspect extends StatefulWidget { - const _NoAspect({Key key}) : super(key: key); + const _NoAspect({Key? key}) : super(key: key); @override _NoAspectState createState() => _NoAspectState(); } @@ -2879,31 +3041,11 @@ class _NoAspectState extends State<_NoAspect> { } } -class _NullAspect extends StatefulWidget { - const _NullAspect({Key key}) : super(key: key); - @override - _NullAspectState createState() => _NullAspectState(); -} - -class _NullAspectState extends State<_NullAspect> { - ValueKey get key => widget.key as ValueKey; - - int _buildCount = 0; - - @override - Widget build(BuildContext context) { - final value = Inheritable.of(context)?.valueFor(null); - final text = User.displayW(key.value, value, _buildCount += 1); - - return Text(text); - } -} - class _ExtractAspectW extends StatefulWidget { final ExtractAspect _extract; const _ExtractAspectW( this._extract, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -2927,7 +3069,7 @@ class _SomeAspectW extends StatefulWidget { final Set> _aspects; const _SomeAspectW( this._aspects, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -2951,7 +3093,7 @@ class _SomeChainedAspectW extends StatefulWidget { final Set> _aspects; const _SomeChainedAspectW( this._aspects, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -2968,7 +3110,7 @@ class _SomeChainedAspectWState extends State<_SomeChainedAspectW> { final aspect = widget._aspects .some() .map((it) => it.toString()) - .where(({prev, next}) => prev != next) + .where(({required prev, required next}) => prev != next) .of(context); final text = User.displayW(key.value, aspect, _buildCount += 1); return Text(text); @@ -2979,7 +3121,7 @@ class _ChainableAspectW extends StatefulWidget { final InheritableAspect aspect; const _ChainableAspectW( this.aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3004,7 +3146,7 @@ class _ExtractMutableAspectW extends StatefulWidget { final ExtractAspect _extract; const _ExtractMutableAspectW( this._extract, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3030,7 +3172,7 @@ class _RemovableAspectW extends StatefulWidget { final Aspect aspect; const _RemovableAspectW( this.aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3050,7 +3192,7 @@ class _RemovableAspectWState extends State<_RemovableAspectW> { mainAxisSize: MainAxisSize.min, children: [ Flexible( - child: FlatButton( + child: TextButton( key: const Key('remove-aspect-button'), onPressed: () => context.aspect.remove(widget.aspect), child: const Text('remove-aspect'), @@ -3066,7 +3208,7 @@ class _RemovableAspectViaKeyW extends StatefulWidget { final Aspect aspect; const _RemovableAspectViaKeyW( this.aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3088,9 +3230,10 @@ class _RemovableAspectViaKeyWState mainAxisSize: MainAxisSize.min, children: [ Flexible( - child: FlatButton( + child: TextButton( key: const Key('remove-aspect-via-key-button'), - onPressed: () => context.aspect.removeKey(widget.aspect.key), + // ignore: deprecated_member_use_from_same_package + onPressed: () => context.aspect.removeKey(widget.aspect.key!), child: const Text('remove-aspect'), ), ), @@ -3107,13 +3250,13 @@ class _DebounceAspectW extends StatefulWidget { final PredicateAspect compare; static const defaultDelay = Duration(milliseconds: 200); - static bool _equals({Object prev, Object next}) { + static bool _equals({required Object? prev, required Object? next}) { return next != prev; } const _DebounceAspectW( this.aspect, { - @required ValueKey key, + required ValueKey key, this.compare = _equals, this.leading = false, this.duration = defaultDelay, @@ -3127,7 +3270,7 @@ class _DebounceAspectWState extends State<_DebounceAspectW> { ValueKey get key => widget.key as ValueKey; int _buildCount = 0; - Aspect aspect; + late Aspect aspect; @override void initState() { @@ -3158,13 +3301,13 @@ class _InlineDebounceAspectW extends StatefulWidget { final PredicateAspect compare; static const defaultDelay = Duration(milliseconds: 200); - static bool _equals({Object prev, Object next}) { + static bool _equals({required Object? prev, required Object? next}) { return next != prev; } const _InlineDebounceAspectW( this.aspect, { - @required ValueKey key, + required ValueKey key, this.compare = _equals, this.duration = defaultDelay, }) : super(key: key); @@ -3202,7 +3345,7 @@ class _OverridenAspectW extends StatefulWidget { final InheritableAspect _aspect; const _OverridenAspectW( this._aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3226,7 +3369,7 @@ class _OverridenMutableAspectW extends StatefulWidget { final MutableInheritableAspect _aspect; const _OverridenMutableAspectW( this._aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override @@ -3243,7 +3386,7 @@ class _OverridenMutableAspectWState @override Widget build(BuildContext context) { final text = User.displayW(key.value, '', _buildCount += 1); - return FlatButton( + return TextButton( key: Key('${widget.key}-button'), onPressed: () => widget._aspect.apply(context), child: Text(text), @@ -3255,7 +3398,7 @@ class _AspectW extends StatefulWidget { final InheritableAspect aspect; const _AspectW( this.aspect, { - @required ValueKey key, + required ValueKey key, }) : super(key: key); @override