Changing themes caused crash The root cause was that we crawled the tree to mark anyone who depended on the updated theme dirty _after_ we crawled it to rebuild it. Thus, if anyone was already marked dirty when the process started, then got marked clean by the first (rebuild) walk, then got marked dirty again by the notification, they'd be clean when they got the notification, despite already being in the dirty list, which would cause an assertion. Also IconTheme didn't have an operator==, so it was independently too aggressive about updates.
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index f92219d..dd36a86 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -7,6 +7,8 @@ import 'package:sky/rendering.dart'; +// KEYS + /// A Key is an identifier for [Widget]s and [Element]s. A new Widget will only /// be used to reconfigure an existing Element if its Key is the same as its /// original Widget's Key. @@ -171,6 +173,8 @@ } +// WIDGETS + /// A Widget object describes the configuration for an [Element]. /// Widget subclasses should be immutable with const constructors. /// Widgets form a tree that is then inflated into an Element tree. @@ -193,6 +197,8 @@ void debugFillDescription(List<String> description) { } } +// TODO(ianh): move the next four classes to below InheritedWidget + /// RenderObjectWidgets provide the configuration for [RenderObjectElement]s, /// which wrap [RenderObject]s, which provide the actual rendering of the /// application. @@ -382,16 +388,14 @@ } } -abstract class ProxyWidget extends StatelessComponent { - const ProxyWidget({ Key key, Widget this.child }) : super(key: key); +abstract class ProxyComponent extends Widget { + const ProxyComponent({ Key key, this.child }) : super(key: key); final Widget child; - - Widget build(BuildContext context) => child; } -abstract class ParentDataWidget extends ProxyWidget { - ParentDataWidget({ Key key, Widget child }) +abstract class ParentDataWidget extends ProxyComponent { + const ParentDataWidget({ Key key, Widget child }) : super(key: key, child: child); ParentDataElement createElement() => new ParentDataElement(this); @@ -405,7 +409,7 @@ void applyParentData(RenderObject renderObject); } -abstract class InheritedWidget extends ProxyWidget { +abstract class InheritedWidget extends ProxyComponent { const InheritedWidget({ Key key, Widget child }) : super(key: key, child: child); @@ -414,6 +418,9 @@ bool updateShouldNotify(InheritedWidget oldWidget); } + +// ELEMENTS + bool _canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; @@ -637,6 +644,7 @@ assert(_debugLifecycleState == _ElementLifecycle.active); assert(widget != null); assert(newWidget != null); + assert(newWidget != widget); assert(depth != null); assert(_active); assert(_canUpdate(widget, newWidget)); @@ -970,8 +978,8 @@ typedef Widget WidgetBuilder(BuildContext context); -/// Base class for the instantiation of StatelessComponent and StatefulComponent -/// widgets. +/// Base class for the instantiation of StatelessComponent, StatefulComponent, +/// and ProxyComponent widgets. abstract class ComponentElement<T extends Widget> extends BuildableElement<T> { ComponentElement(T widget) : super(widget); @@ -1114,7 +1122,26 @@ } } -class ParentDataElement extends StatelessComponentElement<ParentDataWidget> { +abstract class ProxyElement<T extends ProxyComponent> extends ComponentElement<T> { + ProxyElement(T widget) : super(widget) { + _builder = (BuildContext context) => this.widget.child; + } + + void update(T newWidget) { + T oldWidget = widget; + assert(widget != null); + assert(widget != newWidget); + super.update(newWidget); + assert(widget == newWidget); + notifyDescendants(oldWidget); + _dirty = true; + rebuild(); + } + + void notifyDescendants(T oldWidget); +} + +class ParentDataElement extends ProxyElement<ParentDataWidget> { ParentDataElement(ParentDataWidget widget) : super(widget); void mount(Element parent, dynamic slot) { @@ -1134,15 +1161,7 @@ super.mount(parent, slot); } - void update(ParentDataWidget newWidget) { - ParentDataWidget oldWidget = widget; - super.update(newWidget); - assert(widget == newWidget); - if (widget != oldWidget) - _notifyDescendants(); - } - - void _notifyDescendants() { + void notifyDescendants(ParentDataWidget oldWidget) { void notifyChildren(Element child) { if (child is RenderObjectElement) child.updateParentData(widget); @@ -1153,20 +1172,14 @@ } } -class InheritedElement extends StatelessComponentElement<InheritedWidget> { + + +class InheritedElement extends ProxyElement<InheritedWidget> { InheritedElement(InheritedWidget widget) : super(widget); - void update(StatelessComponent newWidget) { - InheritedWidget oldWidget = widget; - super.update(newWidget); - assert(widget == newWidget); - if (widget.updateShouldNotify(oldWidget)) { - assert(widget != oldWidget); - _notifyDescendants(); - } - } - - void _notifyDescendants() { + void notifyDescendants(InheritedWidget oldWidget) { + if (!widget.updateShouldNotify(oldWidget)) + return; final Type ourRuntimeType = widget.runtimeType; void notifyChildren(Element child) { if (child._dependencies != null &&
diff --git a/packages/flutter/lib/src/widgets/icon.dart b/packages/flutter/lib/src/widgets/icon.dart index 5dcee05..9b71b13 100644 --- a/packages/flutter/lib/src/widgets/icon.dart +++ b/packages/flutter/lib/src/widgets/icon.dart
@@ -14,6 +14,9 @@ class IconThemeData { const IconThemeData({ this.color }); final IconThemeColor color; + + bool operator==(other) => other.runtimeType == runtimeType && other.color == color; + int get hashCode => color.hashCode; } class IconTheme extends InheritedWidget {