Revert "InheritedModel - an InheritedWidget for data models (#19739)" (#21523)

This reverts commit 343b57036a94ee9b0131d4d957372a147f8d838d.

Reverting because the assert introduced in framework.dart(inheritFromElement) is breaking certain Mulligan pages. @hixie suspects it might be due to global keys.
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index c42c958..12e2802 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -1870,19 +1870,6 @@
   /// render object is usually short.
   Size get size;
 
-  /// Registers this build context with [ancestor] such that when
-  /// [ancestor]'s widget changes this build context is rebuilt.
-  ///
-  /// Returns `ancestor.widget`.
-  ///
-  /// This method is rarely called directly. Most applications should use
-  /// [inheritFromWidgetOfExactType], which calls this method after finding
-  /// the appropriate [InheritedElement] ancestor.
-  ///
-  /// All of the qualifications about when [inheritFromWidgetOfExactType] can
-  /// be called apply to this method as well.
-  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect });
-
   /// Obtains the nearest widget of the given type, which must be the type of a
   /// concrete [InheritedWidget] subclass, and registers this build context with
   /// that widget such that when that widget changes (or a new widget of that
@@ -1892,7 +1879,7 @@
   /// This is typically called implicitly from `of()` static methods, e.g.
   /// [Theme.of].
   ///
-  /// This method should not be called from widget constructors or from
+  /// This should not be called from widget constructors or from
   /// [State.initState] methods, because those methods would not get called
   /// again if the inherited value were to change. To ensure that the widget
   /// correctly updates itself when the inherited value changes, only call this
@@ -1905,9 +1892,9 @@
   /// It is safe to use this method from [State.deactivate], which is called
   /// whenever the widget is removed from the tree.
   ///
-  /// It is also possible to call this method from interaction event handlers
-  /// (e.g. gesture callbacks) or timers, to obtain a value once, if that value
-  /// is not going to be cached and reused later.
+  /// It is also possible to call this from interaction event handlers (e.g.
+  /// gesture callbacks) or timers, to obtain a value once, if that value is not
+  /// going to be cached and reused later.
   ///
   /// Calling this method is O(1) with a small constant factor, but will lead to
   /// the widget being rebuilt more often.
@@ -1917,12 +1904,7 @@
   /// called, whenever changes occur relating to that widget until the next time
   /// the widget or one of its ancestors is moved (for example, because an
   /// ancestor is added or removed).
-  ///
-  /// The [aspect] parameter is only used when [targetType] is an
-  /// [InheritedWidget] subclasses that supports partial updates, like
-  /// [InheritedModel]. It specifies what "aspect" of the inherited
-  /// widget this context depends on.
-  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect });
+  InheritedWidget inheritFromWidgetOfExactType(Type targetType);
 
   /// Obtains the element corresponding to the nearest widget of the given type,
   /// which must be the type of a concrete [InheritedWidget] subclass.
@@ -3244,31 +3226,15 @@
   }
 
   @override
-  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
-    assert(ancestor != null);
-    assert(() {
-      if (_parent == null) {
-        // We're being deactivated, see deactivateChild()
-        return true;
-      }
-      Element element = _parent;
-      while (ancestor != element && element != null)
-        element = element._parent;
-      return ancestor == element;
-    }());
-    _dependencies ??= new HashSet<InheritedElement>();
-    _dependencies.add(ancestor);
-    ancestor.updateDependencies(this, aspect);
-    return ancestor.widget;
-  }
-
-  @override
-  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
+  InheritedWidget inheritFromWidgetOfExactType(Type targetType) {
     assert(_debugCheckStateIsActiveForAncestorLookup());
     final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
     if (ancestor != null) {
       assert(ancestor is InheritedElement);
-      return inheritFromElement(ancestor, aspect: aspect);
+      _dependencies ??= new HashSet<InheritedElement>();
+      _dependencies.add(ancestor);
+      ancestor._dependents.add(this);
+      return ancestor.widget;
     }
     _hadUnsatisfiedDependencies = true;
     return null;
@@ -3879,13 +3845,11 @@
   }
 
   @override
-  InheritedWidget inheritFromElement(Element ancestor, { Object aspect }) {
-    assert(ancestor != null);
+  InheritedWidget inheritFromWidgetOfExactType(Type targetType) {
     assert(() {
-      final Type targetType = ancestor.widget.runtimeType;
       if (state._debugLifecycleState == _StateLifecycle.created) {
         throw new FlutterError(
-          'inheritFromWidgetOfExactType($targetType) or inheritFromElement() was called before ${_state.runtimeType}.initState() completed.\n'
+          'inheritFromWidgetOfExactType($targetType) was called before ${_state.runtimeType}.initState() completed.\n'
           'When an inherited widget changes, for example if the value of Theme.of() changes, '
           'its dependent widgets are rebuilt. If the dependent widget\'s reference to '
           'the inherited widget is in a constructor or an initState() method, '
@@ -3898,7 +3862,7 @@
       }
       if (state._debugLifecycleState == _StateLifecycle.defunct) {
         throw new FlutterError(
-          'inheritFromWidgetOfExactType($targetType) or inheritFromElement() was called after dispose(): $this\n'
+          'inheritFromWidgetOfExactType($targetType) called after dispose(): $this\n'
           'This error happens if you call inheritFromWidgetOfExactType() on the '
           'BuildContext for a widget that no longer appears in the widget tree '
           '(e.g., whose parent widget no longer includes the widget in its '
@@ -3918,7 +3882,7 @@
       }
       return true;
     }());
-    return super.inheritFromElement(ancestor, aspect: aspect);
+    return super.inheritFromWidgetOfExactType(targetType);
   }
 
   @override
@@ -4068,7 +4032,7 @@
   @override
   InheritedWidget get widget => super.widget;
 
-  final Map<Element, Object> _dependents = new HashMap<Element, Object>();
+  final Set<Element> _dependents = new HashSet<Element>();
 
   @override
   void _updateInheritance() {
@@ -4090,110 +4054,6 @@
     super.debugDeactivated();
   }
 
-  /// Returns the dependencies value recorded for [dependent]
-  /// with [setDependencies].
-  ///
-  /// Each dependent element is mapped to a single object value
-  /// which represents how the element depends on this
-  /// [InheritedElement]. This value is null by default and by default
-  /// dependent elements are rebuilt unconditionally.
-  ///
-  /// Subclasses can manage these values with [updateDependencies]
-  /// so that they can selectively rebuild dependents in
-  /// [notifyDependents].
-  ///
-  /// This method is typically only called in overrides of [updateDependencies].
-  ///
-  /// See also:
-  ///
-  ///  * [updateDependencies], which is called each time a dependency is
-  ///    created with [inheritFromWidgetOfExactType].
-  ///  * [setDependencies], which sets dependencies value for a dependent
-  ///    element.
-  ///  * [notifyDependent], which can be overridden to use a dependent's
-  ///    dependencies value to decide if the dependent needs to be rebuilt.
-  ///  * [InheritedModel], which is an example of a class that uses this method
-  ///    to manage dependency values.
-  @protected
-  Object getDependencies(Element dependent) {
-    return _dependents[dependent];
-  }
-
-  /// Sets the value returned by [getDependencies] value for [dependent].
-  ///
-  /// Each dependent element is mapped to a single object value
-  /// which represents how the element depends on this
-  /// [InheritedElement]. The [updateDependencies] method sets this value to
-  /// null by default so that dependent elements are rebuilt unconditionally.
-  ///
-  /// Subclasses can manage these values with [updateDependencies]
-  /// so that they can selectively rebuild dependents in [notifyDependents].
-  ///
-  /// This method is typically only called in overrides of [updateDependencies].
-  ///
-  /// See also:
-  ///
-  ///  * [updateDependencies], which is called each time a dependency is
-  ///    created with [inheritFromWidgetOfExactType].
-  ///  * [getDependencies], which returns the current value for a dependent
-  ///    element.
-  ///  * [notifyDependent], which can be overridden to use a dependent's
-  ///    [getDependencies] value to decide if the dependent needs to be rebuilt.
-  ///  * [InheritedModel], which is an example of a class that uses this method
-  ///    to manage dependency values.
-  @protected
-  void setDependencies(Element dependent, Object value) {
-    _dependents[dependent] = value;
-  }
-
-  /// Called by [inheritFromWidgetOfExactType] when a new [dependent] is added.
-  ///
-  /// Each dependent element can be mapped to a single object value with
-  /// [setDependencies]. This method can lookup the existing dependencies with
-  /// [getDependencies].
-  ///
-  /// By default this method sets the inherited dependencies for [dependent]
-  /// to null. This only serves to record an unconditional dependency on
-  /// [dependent].
-  ///
-  /// Subclasses can manage their own dependencies values so that they
-  /// can selectively rebuild dependents in [notifyDependents].
-  ///
-  /// See also:
-  ///
-  ///  * [getDependencies], which returns the current value for a dependent
-  ///    element.
-  ///  * [setDependencies], which sets the value for a dependent element.
-  ///  * [notifyDependent], which can be overridden to use a dependent's
-  ///    dependencies value to decide if the dependent needs to be rebuilt.
-  ///  * [InheritedModel], which is an example of a class that uses this method
-  ///    to manage dependency values.
-  @protected
-  void updateDependencies(Element dependent, Object aspect) {
-    setDependencies(dependent, null);
-  }
-
-  /// Called by [notifyClients] for each dependent.
-  ///
-  /// Calls `dependent.didChangeDependencies()` by default.
-  ///
-  /// Subclasses can override this method to selectively call
-  /// [didChangeDependencies] based on the value of [getDependencies].
-  ///
-  /// See also:
-  ///
-  ///  * [updateDependencies], which is called each time a dependency is
-  ///    created with [inheritFromWidgetOfExactType].
-  ///  * [getDependencies], which returns the current value for a dependent
-  ///    element.
-  ///  * [setDependencies], which sets the value for a dependent element.
-  ///  * [InheritedModel], which is an example of a class that uses this method
-  ///    to manage dependency values.
-  @protected
-  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
-    dependent.didChangeDependencies();
-  }
-
   /// Calls [Element.didChangeDependencies] of all dependent elements, if
   /// [InheritedWidget.updateShouldNotify] returns true.
   ///
@@ -4210,7 +4070,7 @@
     if (!widget.updateShouldNotify(oldWidget))
       return;
     assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
-    for (Element dependent in _dependents.keys) {
+    for (Element dependent in _dependents) {
       assert(() {
         // check that it really is our descendant
         Element ancestor = dependent._parent;
@@ -4220,7 +4080,7 @@
       }());
       // check that it really depends on us
       assert(dependent._dependencies.contains(this));
-      notifyDependent(oldWidget, dependent);
+      dependent.didChangeDependencies();
     }
   }
 }
diff --git a/packages/flutter/lib/src/widgets/inherited_model.dart b/packages/flutter/lib/src/widgets/inherited_model.dart
deleted file mode 100644
index 87dcf60..0000000
--- a/packages/flutter/lib/src/widgets/inherited_model.dart
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:collection';
-
-import 'package:flutter/foundation.dart';
-
-import 'framework.dart';
-
-/// An [InheritedWidget] that's intended to be used as the base class for
-/// models whose dependents may only depend on one part or "aspect" of the
-/// overall model.
-///
-/// An inherited widget's dependents are unconditionally rebuilt when the
-/// inherited widget changes per [InheritedWidget.updateShouldNotify].
-/// This widget is similar except that dependents aren't rebuilt
-/// unconditionally.
-///
-/// Widgets that depend on an [InheritedModel] qualify their dependence
-/// with a value that indicates what "aspect" of the model they depend
-/// on. When the model is rebuilt, dependents will also be rebuilt, but
-/// only if there was a change in the model that corresponds to the aspect
-/// they provided.
-///
-/// The type parameter `T` is the type of the model aspect objects.
-///
-/// Widgets create a dependency on an [InheritedModel] with a static method:
-/// [InheritedModel.inheritFrom]. This method's `context` parameter
-/// defines the subtree that will be rebuilt when the model changes.
-/// Typically the `inheritFrom` method is called from a model-specific
-/// static `of` method. For example:
-///
-/// ```dart
-/// class MyModel extends InheritedModel<String> {
-///   // ...
-///   static MyModel of(BuildContext context, String aspect) {
-///     return InheritedModel.inheritFrom<MyModel>(context, aspect: aspect);
-///   }
-/// }
-/// ```
-///
-/// Calling `MyModel.of(context, 'foo')` means that `context` should only
-/// be rebuilt when the `foo` aspect of `MyModel` changes. If the aspect
-/// is null, then the model supports all aspects.
-///
-/// When the inherited model is rebuilt the [updateShouldNotify] and
-/// [updateShouldNotifyDependent] methods are used to decide what
-/// should be rebuilt.  If [updateShouldNotify] returns true, then the
-/// inherited model's [updateShouldNotifyDependent] method is tested for
-/// each dependent and the set of aspect objects it depends on.
-/// The [updateShouldNotifyDependent] method must compare the set of aspect
-/// dependencies with the changes in the model itself.
-///
-/// For example:
-///
-/// ```dart
-/// class ABModel extends InheritedModel<String> {
-///   ABModel({ this.a, this.b, Widget child }) : super(child: child);
-///
-///   final int a;
-///   final int b;
-///
-///   @override
-///   bool updateShouldNotify(ABModel old) {
-///     return a != old.a || b != old.b;
-///   }
-///
-///   @override
-///   bool updateShouldNotifyDependent(ABModel old, Set<String> aspects) {
-///     return (a != old.a && aspects.contains('a'))
-///         || (b != old.b && aspects.contains('b'))
-///   }
-///
-///   // ...
-/// }
-/// ```
-///
-/// In the previous example the dependencies checked by
-/// [updateShouldNotifyDependent] are just the aspect strings passed to
-/// `inheritFromWidgetOfExactType`. They're represented as a [Set] because
-/// one Widget can depend on more than one aspect of the model.
-/// If a widget depends on the model but doesn't specify an aspect,
-/// then changes in the model will cause the widget to be rebuilt
-/// unconditionally.
-abstract class InheritedModel<T> extends InheritedWidget {
-  /// Creates an inherited widget that supports dependencies qualified by
-  /// "aspects", i.e. a descendant widget can indicate that it should
-  /// only be rebuilt if a specific aspect of the model changes.
-  const InheritedModel({ Key key, Widget child }) : super(key: key, child: child);
-
-  @override
-  InheritedModelElement<T> createElement() => new InheritedModelElement<T>(this);
-
-  /// Return true if the changes between this model and [oldWidget] match any
-  /// of the [dependencies].
-  @protected
-  bool updateShouldNotifyDependent(covariant InheritedModel<T> oldWidget, Set<T> dependencies);
-
-  /// Returns true if this model supports the given [aspect].
-  ///
-  /// Returns true by default: this model supports all aspects.
-  ///
-  /// Subclasses may override this method to indicate that they do not support
-  /// all model aspects. This is typically done when a model can be used
-  /// to "shadow" some aspects of an ancestor.
-  @protected
-  bool isSupportedAspect(Object aspect) => true;
-
-  // The [result] will be a list of all of context's type T ancestors concluding
-  // with the one that supports the specified model [aspect].
-  static Iterable<InheritedElement> _findModels<T extends InheritedModel<Object>>(BuildContext context, Object aspect) sync* {
-    final InheritedElement model = context.ancestorInheritedElementForWidgetOfExactType(T);
-    if (model == null)
-      return;
-
-    yield model;
-
-    assert(model.widget is T);
-    final T modelWidget = model.widget;
-    if (modelWidget.isSupportedAspect(aspect))
-      return;
-
-    Element modelParent;
-    model.visitAncestorElements((Element ancestor) {
-      modelParent = ancestor;
-      return false;
-    });
-    if (modelParent == null)
-      return;
-
-    yield* _findModels<T>(modelParent, aspect);
-  }
-
-  /// Makes [context] dependent on the specified [aspect] of an [InheritedModel]
-  /// of type T.
-  ///
-  /// When the given [aspect] of the model changes, the [context] will be
-  /// rebuilt. The [updateShouldNotifyDependent] method must determine if a
-  /// change in the model widget corresponds to an [aspect] value.
-  ///
-  /// The dependencies created by this method target all [InheritedModel] ancestors
-  /// of type T up to and including the first one for which [isSupportedAspect]
-  /// returns true.
-  ///
-  /// If [aspect] is null this method is the same as
-  /// `context.inheritFromWidgetOfExactType(T)`.
-  static T inheritFrom<T extends InheritedModel<Object>>(BuildContext context, { Object aspect }) {
-    if (aspect == null)
-      return context.inheritFromWidgetOfExactType(T);
-
-    // Create a dependency on all of the type T ancestor models up until
-    // a model is found for which isSupportedAspect(aspect) is true.
-    final List<InheritedElement> models = _findModels<T>(context, aspect).toList();
-    final InheritedElement lastModel = models.last;
-    for (InheritedElement model in models) {
-      final T value = context.inheritFromElement(model, aspect: aspect);
-      if (model == lastModel)
-        return value;
-    }
-
-    assert(false);
-    return null;
-  }
-}
-
-/// An [Element] that uses a [InheritedModel] as its configuration.
-class InheritedModelElement<T> extends InheritedElement {
-  /// Creates an element that uses the given widget as its configuration.
-  InheritedModelElement(InheritedModel<T> widget) : super(widget);
-
-  @override
-  InheritedModel<T> get widget => super.widget;
-
-  @override
-  void updateDependencies(Element dependent, Object aspect) {
-    final Set<T> dependencies = getDependencies(dependent);
-    if (dependencies != null && dependencies.isEmpty)
-      return;
-
-    if (aspect == null) {
-      setDependencies(dependent, new HashSet<T>());
-    } else {
-      assert(aspect is T);
-      setDependencies(dependent, (dependencies ?? new HashSet<T>())..add(aspect));
-    }
-  }
-
-  @override
-  void notifyDependent(InheritedModel<T> oldWidget, Element dependent) {
-    final Set<T> dependencies = getDependencies(dependent);
-    if (dependencies == null)
-      return;
-    if (dependencies.isEmpty || widget.updateShouldNotifyDependent(oldWidget, dependencies))
-      dependent.didChangeDependencies();
-  }
-}
diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart
index 8dc2fbc..72920b0 100644
--- a/packages/flutter/lib/widgets.dart
+++ b/packages/flutter/lib/widgets.dart
@@ -46,7 +46,6 @@
 export 'src/widgets/image.dart';
 export 'src/widgets/image_icon.dart';
 export 'src/widgets/implicit_animations.dart';
-export 'src/widgets/inherited_model.dart';
 export 'src/widgets/layout_builder.dart';
 export 'src/widgets/list_wheel_scroll_view.dart';
 export 'src/widgets/localizations.dart';
diff --git a/packages/flutter/test/widgets/inherited_model.dart b/packages/flutter/test/widgets/inherited_model.dart
deleted file mode 100644
index e69de29..0000000
--- a/packages/flutter/test/widgets/inherited_model.dart
+++ /dev/null
diff --git a/packages/flutter/test/widgets/inherited_model_test.dart b/packages/flutter/test/widgets/inherited_model_test.dart
deleted file mode 100644
index aec404f..0000000
--- a/packages/flutter/test/widgets/inherited_model_test.dart
+++ /dev/null
@@ -1,461 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-
-// A simple "flat" InheritedModel: the data model is just 3 integer
-// valued fields: a, b, c.
-class ABCModel extends InheritedModel<String> {
-  const ABCModel({
-    Key key,
-    this.a,
-    this.b,
-    this.c,
-    this.aspects,
-    Widget child,
-  }) : super(key: key, child: child);
-
-  final int a;
-  final int b;
-  final int c;
-
-  // The aspects (fields) of this model that widgets can depend on with
-  // inheritFrom.
-  //
-  // This property is null by default, which means that the model supports
-  // all 3 fields.
-  final Set<String> aspects;
-
-  @override
-  bool isSupportedAspect(Object aspect) {
-    return aspect == null || aspects == null || aspects.contains(aspect);
-  }
-
-  @override
-  bool updateShouldNotify(ABCModel old) {
-    return !setEquals<String>(aspects, old.aspects) || a != old.a || b != old.b || c != old.c;
-  }
-
-  @override
-  bool updateShouldNotifyDependent(ABCModel old, Set<String> dependencies) {
-    return !setEquals<String>(aspects, old.aspects)
-        || (a != old.a && dependencies.contains('a'))
-        || (b != old.b && dependencies.contains('b'))
-        || (c != old.c && dependencies.contains('c'));
-  }
-
-  static ABCModel of(BuildContext context, { String fieldName }) {
-    return InheritedModel.inheritFrom<ABCModel>(context, aspect: fieldName);
-  }
-}
-
-class ShowABCField extends StatefulWidget {
-  const ShowABCField({ Key key, this.fieldName }) : super(key: key);
-
-  final String fieldName;
-
-  @override
-  _ShowABCFieldState createState() => new _ShowABCFieldState();
-}
-
-class _ShowABCFieldState extends State<ShowABCField> {
-  int _buildCount = 0;
-
-  @override
-  Widget build(BuildContext context) {
-    final ABCModel abc = ABCModel.of(context, fieldName: widget.fieldName);
-    final int value = widget.fieldName == 'a' ? abc.a : (widget.fieldName == 'b' ? abc.b : abc.c);
-    return new Text('${widget.fieldName}: $value [${_buildCount++}]');
-  }
-}
-
-void main() {
-  testWidgets('InheritedModel basics', (WidgetTester tester) async {
-    int _a = 0;
-    int _b = 1;
-    int _c = 2;
-
-    final Widget abcPage = new StatefulBuilder(
-      builder: (BuildContext context, StateSetter setState) {
-        const Widget showA = ShowABCField(fieldName: 'a');
-        const Widget showB = ShowABCField(fieldName: 'b');
-        const Widget showC = ShowABCField(fieldName: 'c');
-
-        // Unconditionally depends on the ABCModel: rebuilt when any
-        // aspect of the model changes.
-        final Widget showABC = new Builder(
-          builder: (BuildContext context) {
-            final ABCModel abc = ABCModel.of(context);
-            return new Text('a: ${abc.a} b: ${abc.b} c: ${abc.c}');
-          }
-        );
-
-        return new Scaffold(
-          body: new StatefulBuilder(
-            builder: (BuildContext context, StateSetter setState) {
-              return new ABCModel(
-                a: _a,
-                b: _b,
-                c: _c,
-                child: new Center(
-                  child: new Column(
-                    mainAxisSize: MainAxisSize.min,
-                    children: <Widget>[
-                      showA,
-                      showB,
-                      showC,
-                      showABC,
-                      new RaisedButton(
-                        child: const Text('Increment a'),
-                        onPressed: () {
-                          // Rebuilds the ABCModel which triggers a rebuild
-                          // of showA because showA depends on the 'a' aspect
-                          // of the ABCModel.
-                          setState(() { _a += 1; });
-                        },
-                      ),
-                      new RaisedButton(
-                        child: const Text('Increment b'),
-                        onPressed: () {
-                          // Rebuilds the ABCModel which triggers a rebuild
-                          // of showB because showB depends on the 'b' aspect
-                          // of the ABCModel.
-                          setState(() { _b += 1; });
-                        },
-                      ),
-                      new RaisedButton(
-                        child: const Text('Increment c'),
-                        onPressed: () {
-                          // Rebuilds the ABCModel which triggers a rebuild
-                          // of showC because showC depends on the 'c' aspect
-                          // of the ABCModel.
-                          setState(() { _c += 1; });
-                        },
-                      ),
-                    ],
-                  ),
-                ),
-              );
-            },
-          ),
-        );
-      },
-    );
-
-    await tester.pumpWidget(new MaterialApp(home: abcPage));
-
-    expect(find.text('a: 0 [0]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 0 b: 1 c: 2'), findsOneWidget);
-
-    await tester.tap(find.text('Increment a'));
-    await tester.pumpAndSettle();
-    // Verify that field 'a' was incremented, but only the showA
-    // and showABC widgets were rebuilt.
-    expect(find.text('a: 1 [1]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 1 b: 1 c: 2'), findsOneWidget);
-
-    // Verify that field 'a' was incremented, but only the showA
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment a'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 2 [2]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 2 b: 1 c: 2'), findsOneWidget);
-
-    // Verify that field 'b' was incremented, but only the showB
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment b'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 2 [2]'), findsOneWidget);
-    expect(find.text('b: 2 [1]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 2 b: 2 c: 2'), findsOneWidget);
-
-    // Verify that field 'c' was incremented, but only the showC
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment c'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 2 [2]'), findsOneWidget);
-    expect(find.text('b: 2 [1]'), findsOneWidget);
-    expect(find.text('c: 3 [1]'), findsOneWidget);
-    expect(find.text('a: 2 b: 2 c: 3'), findsOneWidget);
-  });
-
-  testWidgets('Inner InheritedModel shadows the outer one', (WidgetTester tester) async {
-    int _a = 0;
-    int _b = 1;
-    int _c = 2;
-
-    // Same as in abcPage in the "InheritedModel basics" test except:
-    // there are two ABCModels and the inner model's "a" and "b"
-    // properties shadow (override) the outer model. Further complicating
-    // matters: the inner model only supports the model's "a" aspect,
-    // so showB and showC will depend on the outer model.
-    final Widget abcPage = new StatefulBuilder(
-      builder: (BuildContext context, StateSetter setState) {
-        const Widget showA = ShowABCField(fieldName: 'a');
-        const Widget showB = ShowABCField(fieldName: 'b');
-        const Widget showC = ShowABCField(fieldName: 'c');
-
-        // Unconditionally depends on the closest ABCModel ancestor.
-        // Which is the inner model, for which b,c are null.
-        final Widget showABC = new Builder(
-          builder: (BuildContext context) {
-            final ABCModel abc = ABCModel.of(context);
-            return new Text('a: ${abc.a} b: ${abc.b} c: ${abc.c}', style: Theme.of(context).textTheme.title);
-          }
-        );
-
-        return new Scaffold(
-          body: new StatefulBuilder(
-            builder: (BuildContext context, StateSetter setState) {
-              return new ABCModel( // The "outer" model
-                a: _a,
-                b: _b,
-                c: _c,
-                child: new ABCModel( // The "inner" model
-                  a: 100 + _a,
-                  b: 100 + _b,
-                  aspects: new Set<String>.of(<String>['a']),
-                  child: new Center(
-                    child: new Column(
-                      mainAxisSize: MainAxisSize.min,
-                      children: <Widget>[
-                        showA,
-                        showB,
-                        showC,
-                        const SizedBox(height: 24.0),
-                        showABC,
-                        const SizedBox(height: 24.0),
-                        new RaisedButton(
-                          child: const Text('Increment a'),
-                          onPressed: () {
-                            setState(() { _a += 1; });
-                          },
-                        ),
-                        new RaisedButton(
-                          child: const Text('Increment b'),
-                          onPressed: () {
-                            setState(() { _b += 1; });
-                          },
-                        ),
-                        new RaisedButton(
-                          child: const Text('Increment c'),
-                          onPressed: () {
-                            setState(() { _c += 1; });
-                          },
-                        ),
-                      ],
-                    ),
-                  ),
-                ),
-              );
-            },
-          ),
-        );
-      },
-    );
-
-    await tester.pumpWidget(new MaterialApp(home: abcPage));
-    expect(find.text('a: 100 [0]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 100 b: 101 c: null'), findsOneWidget);
-
-    await tester.tap(find.text('Increment a'));
-    await tester.pumpAndSettle();
-    // Verify that field 'a' was incremented, but only the showA
-    // and showABC widgets were rebuilt.
-    expect(find.text('a: 101 [1]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 101 b: 101 c: null'), findsOneWidget);
-
-    await tester.tap(find.text('Increment a'));
-    await tester.pumpAndSettle();
-    // Verify that field 'a' was incremented, but only the showA
-    // and showABC widgets were rebuilt.
-    expect(find.text('a: 102 [2]'), findsOneWidget);
-    expect(find.text('b: 1 [0]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 102 b: 101 c: null'), findsOneWidget);
-
-    // Verify that field 'b' was incremented, but only the showB
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment b'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 102 [2]'), findsOneWidget);
-    expect(find.text('b: 2 [1]'), findsOneWidget);
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 102 b: 102 c: null'), findsOneWidget);
-
-    // Verify that field 'c' was incremented, but only the showC
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment c'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 102 [2]'), findsOneWidget);
-    expect(find.text('b: 2 [1]'), findsOneWidget);
-    expect(find.text('c: 3 [1]'), findsOneWidget);
-    expect(find.text('a: 102 b: 102 c: null'), findsOneWidget);
-  });
-
-  testWidgets('InheritedModel inner models supported aspect change', (WidgetTester tester) async {
-    int _a = 0;
-    int _b = 1;
-    int _c = 2;
-    Set<String> _innerModelAspects = new Set<String>.of(<String>['a']);
-
-    // Same as in abcPage in the "Inner InheritedModel shadows the outer one"
-    // test except: the "Add b aspect" changes adds 'b' to the set of
-    // aspects supported by the inner model.
-    final Widget abcPage = new StatefulBuilder(
-      builder: (BuildContext context, StateSetter setState) {
-        const Widget showA = ShowABCField(fieldName: 'a');
-        const Widget showB = ShowABCField(fieldName: 'b');
-        const Widget showC = ShowABCField(fieldName: 'c');
-
-        // Unconditionally depends on the closest ABCModel ancestor.
-        // Which is the inner model, for which b,c are null.
-        final Widget showABC = new Builder(
-          builder: (BuildContext context) {
-            final ABCModel abc = ABCModel.of(context);
-            return new Text('a: ${abc.a} b: ${abc.b} c: ${abc.c}', style: Theme.of(context).textTheme.title);
-          }
-        );
-
-        return new Scaffold(
-          body: new StatefulBuilder(
-            builder: (BuildContext context, StateSetter setState) {
-              return new ABCModel( // The "outer" model
-                a: _a,
-                b: _b,
-                c: _c,
-                child: new ABCModel( // The "inner" model
-                  a: 100 + _a,
-                  b: 100 + _b,
-                  aspects: _innerModelAspects,
-                  child: new Center(
-                    child: new Column(
-                      mainAxisSize: MainAxisSize.min,
-                      children: <Widget>[
-                        showA,
-                        showB,
-                        showC,
-                        const SizedBox(height: 24.0),
-                        showABC,
-                        const SizedBox(height: 24.0),
-                        new RaisedButton(
-                          child: const Text('Increment a'),
-                          onPressed: () {
-                            setState(() { _a += 1; });
-                          },
-                        ),
-                        new RaisedButton(
-                          child: const Text('Increment b'),
-                          onPressed: () {
-                            setState(() { _b += 1; });
-                          },
-                        ),
-                        new RaisedButton(
-                          child: const Text('Increment c'),
-                          onPressed: () {
-                            setState(() { _c += 1; });
-                          },
-                        ),
-                        new RaisedButton(
-                          child: const Text('rebuild'),
-                          onPressed: () {
-                            setState(() {
-                              // Rebuild both models
-                            });
-                          },
-                        ),
-                      ],
-                    ),
-                  ),
-                ),
-              );
-            },
-          ),
-        );
-      },
-    );
-
-    _innerModelAspects = new Set<String>.of(<String>['a']);
-    await tester.pumpWidget(new MaterialApp(home: abcPage));
-    expect(find.text('a: 100 [0]'), findsOneWidget); // showA depends on the inner model
-    expect(find.text('b: 1 [0]'), findsOneWidget); // showB depends on the outer model
-    expect(find.text('c: 2 [0]'), findsOneWidget);
-    expect(find.text('a: 100 b: 101 c: null'), findsOneWidget); // inner model's a, b, c
-
-    _innerModelAspects = new Set<String>.of(<String>['a', 'b']);
-    await tester.tap(find.text('rebuild'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 100 [1]'), findsOneWidget); // rebuilt showA still depend on the inner model
-    expect(find.text('b: 101 [1]'), findsOneWidget); // rebuilt showB now depends on the inner model
-    expect(find.text('c: 2 [1]'), findsOneWidget); // rebuilt showC still depends on the outer model
-    expect(find.text('a: 100 b: 101 c: null'), findsOneWidget); // inner model's a, b, c
-
-    // Verify that field 'a' was incremented, but only the showA
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment a'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 101 [2]'), findsOneWidget); // rebuilt showA still depends on the inner model
-    expect(find.text('b: 101 [1]'), findsOneWidget);
-    expect(find.text('c: 2 [1]'), findsOneWidget);
-    expect(find.text('a: 101 b: 101 c: null'), findsOneWidget);
-
-    // Verify that field 'b' was incremented, but only the showB
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment b'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 101 [2]'), findsOneWidget); // rebuilt showB still depends on the inner model
-    expect(find.text('b: 102 [2]'), findsOneWidget);
-    expect(find.text('c: 2 [1]'), findsOneWidget);
-    expect(find.text('a: 101 b: 102 c: null'), findsOneWidget);
-
-    // Verify that field 'c' was incremented, but only the showC
-    // and showABC widgets were rebuilt.
-    await tester.tap(find.text('Increment c'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 101 [2]'), findsOneWidget);
-    expect(find.text('b: 102 [2]'), findsOneWidget);
-    expect(find.text('c: 3 [2]'), findsOneWidget); // rebuilt showC still depends on the outer model
-    expect(find.text('a: 101 b: 102 c: null'), findsOneWidget);
-
-    _innerModelAspects = new Set<String>.of(<String>['a', 'b', 'c']);
-    await tester.tap(find.text('rebuild'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 101 [3]'), findsOneWidget); // rebuilt showA still depend on the inner model
-    expect(find.text('b: 102 [3]'), findsOneWidget); // rebuilt showB still depends on the inner model
-    expect(find.text('c: null [3]'), findsOneWidget); // rebuilt showC now depends on the inner model
-    expect(find.text('a: 101 b: 102 c: null'), findsOneWidget); // inner model's a, b, c
-
-    // Now the inner model supports no aspects
-    _innerModelAspects = new Set<String>.of(<String>[]);
-    await tester.tap(find.text('rebuild'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 1 [4]'), findsOneWidget); // rebuilt showA now depends on the outer model
-    expect(find.text('b: 2 [4]'), findsOneWidget); // rebuilt showB now depends on the outer model
-    expect(find.text('c: 3 [4]'), findsOneWidget); // rebuilt showC now depends on the outer model
-    expect(find.text('a: 101 b: 102 c: null'), findsOneWidget); // inner model's a, b, c
-
-    // Now the inner model supports all aspects
-    _innerModelAspects = null;
-    await tester.tap(find.text('rebuild'));
-    await tester.pumpAndSettle();
-    expect(find.text('a: 101 [5]'), findsOneWidget); // rebuilt showA now depends on the inner model
-    expect(find.text('b: 102 [5]'), findsOneWidget); // rebuilt showB now depends on the inner model
-    expect(find.text('c: null [5]'), findsOneWidget); // rebuilt showC now depends on the inner model
-    expect(find.text('a: 101 b: 102 c: null'), findsOneWidget); // inner model's a, b, c
-  });
-}