Commit 1e63dc4a authored by Adam Barth's avatar Adam Barth Committed by GitHub

Merge BuildableElement and Element (#8758)

There aren't any subclasses of Element that don't also subclass
BuildableElement and I suspect they wouldn't work properly anyway.

Fixes #3656
parent 146fc617
...@@ -35,7 +35,7 @@ Future<Null> main() async { ...@@ -35,7 +35,7 @@ Future<Null> main() async {
await tester.pump(); // Start drawer animation await tester.pump(); // Start drawer animation
await tester.pump(const Duration(seconds: 1)); // Complete drawer animation await tester.pump(const Duration(seconds: 1)); // Complete drawer animation
final BuildableElement appState = tester.element(find.byType(stocks.StocksApp)); final Element appState = tester.element(find.byType(stocks.StocksApp));
watch.start(); watch.start();
while (watch.elapsed < kBenchmarkTime) { while (watch.elapsed < kBenchmarkTime) {
......
...@@ -1700,13 +1700,13 @@ class BuildOwner { ...@@ -1700,13 +1700,13 @@ class BuildOwner {
final _InactiveElements _inactiveElements = new _InactiveElements(); final _InactiveElements _inactiveElements = new _InactiveElements();
final List<BuildableElement> _dirtyElements = <BuildableElement>[]; final List<Element> _dirtyElements = <Element>[];
bool _scheduledFlushDirtyElements = false; bool _scheduledFlushDirtyElements = false;
bool _dirtyElementsNeedsResorting; // null means we're not in a buildScope bool _dirtyElementsNeedsResorting; // null means we're not in a buildScope
/// Adds an element to the dirty elements list so that it will be rebuilt /// Adds an element to the dirty elements list so that it will be rebuilt
/// when [WidgetsBinding.beginFrame] calls [buildScope]. /// when [WidgetsBinding.beginFrame] calls [buildScope].
void scheduleBuildFor(BuildableElement element) { void scheduleBuildFor(Element element) {
assert(element != null); assert(element != null);
assert(element.owner == this); assert(element.owner == this);
assert(() { assert(() {
...@@ -1758,7 +1758,7 @@ class BuildOwner { ...@@ -1758,7 +1758,7 @@ class BuildOwner {
int _debugStateLockLevel = 0; int _debugStateLockLevel = 0;
bool get _debugStateLocked => _debugStateLockLevel > 0; bool get _debugStateLocked => _debugStateLockLevel > 0;
bool _debugBuilding = false; bool _debugBuilding = false;
BuildableElement _debugCurrentBuildTarget; Element _debugCurrentBuildTarget;
/// Establishes a scope in which calls to [State.setState] are forbidden, and /// Establishes a scope in which calls to [State.setState] are forbidden, and
/// calls the given `callback`. /// calls the given `callback`.
...@@ -1803,7 +1803,7 @@ class BuildOwner { ...@@ -1803,7 +1803,7 @@ class BuildOwner {
/// Only one [buildScope] can be active at a time. /// Only one [buildScope] can be active at a time.
/// ///
/// A [buildScope] implies a [lockState] scope as well. /// A [buildScope] implies a [lockState] scope as well.
void buildScope(BuildableElement context, [VoidCallback callback]) { void buildScope(Element context, [VoidCallback callback]) {
if (callback == null && _dirtyElements.isEmpty) if (callback == null && _dirtyElements.isEmpty)
return; return;
assert(context != null); assert(context != null);
...@@ -1820,9 +1820,8 @@ class BuildOwner { ...@@ -1820,9 +1820,8 @@ class BuildOwner {
try { try {
_scheduledFlushDirtyElements = true; _scheduledFlushDirtyElements = true;
if (callback != null) { if (callback != null) {
assert(context is BuildableElement);
assert(_debugStateLocked); assert(_debugStateLocked);
BuildableElement debugPreviousBuildTarget; Element debugPreviousBuildTarget;
assert(() { assert(() {
context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true); context._debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
debugPreviousBuildTarget = _debugCurrentBuildTarget; debugPreviousBuildTarget = _debugCurrentBuildTarget;
...@@ -1879,7 +1878,7 @@ class BuildOwner { ...@@ -1879,7 +1878,7 @@ class BuildOwner {
} }
} }
assert(() { assert(() {
if (_dirtyElements.any((BuildableElement element) => element._active && element.dirty)) { if (_dirtyElements.any((Element element) => element._active && element.dirty)) {
throw new FlutterError( throw new FlutterError(
'buildScope missed some dirty elements.\n' 'buildScope missed some dirty elements.\n'
'This probably indicates that the dirty list should have been resorted but was not.\n' 'This probably indicates that the dirty list should have been resorted but was not.\n'
...@@ -1890,7 +1889,7 @@ class BuildOwner { ...@@ -1890,7 +1889,7 @@ class BuildOwner {
return true; return true;
}); });
} finally { } finally {
for (BuildableElement element in _dirtyElements) { for (Element element in _dirtyElements) {
assert(element._inDirtyList); assert(element._inDirtyList);
element._inDirtyList = false; element._inDirtyList = false;
} }
...@@ -2115,9 +2114,6 @@ abstract class Element implements BuildContext { ...@@ -2115,9 +2114,6 @@ abstract class Element implements BuildContext {
int get depth => _depth; int get depth => _depth;
int _depth; int _depth;
/// Returns true if the element has been marked as needing rebuilding.
bool get dirty => false;
static int _sort(Element a, Element b) { static int _sort(Element a, Element b) {
if (a.depth < b.depth) if (a.depth < b.depth)
return -1; return -1;
...@@ -2141,7 +2137,9 @@ abstract class Element implements BuildContext { ...@@ -2141,7 +2137,9 @@ abstract class Element implements BuildContext {
bool _active = false; bool _active = false;
@mustCallSuper
void _reassemble() { void _reassemble() {
markNeedsBuild();
visitChildren((Element child) { visitChildren((Element child) {
child._reassemble(); child._reassemble();
}); });
...@@ -2554,6 +2552,7 @@ abstract class Element implements BuildContext { ...@@ -2554,6 +2552,7 @@ abstract class Element implements BuildContext {
assert(owner != null); assert(owner != null);
assert(depth != null); assert(depth != null);
assert(!_active); assert(!_active);
final bool hadDependencies = ((_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies);
_active = true; _active = true;
// We unregistered our dependencies in deactivate, but never cleared the list. // We unregistered our dependencies in deactivate, but never cleared the list.
// Since we're going to be reused, let's clear our list now. // Since we're going to be reused, let's clear our list now.
...@@ -2561,6 +2560,12 @@ abstract class Element implements BuildContext { ...@@ -2561,6 +2560,12 @@ abstract class Element implements BuildContext {
_hadUnsatisfiedDependencies = false; _hadUnsatisfiedDependencies = false;
_updateInheritance(); _updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }); assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; });
if (_dirty)
owner.scheduleBuildFor(this);
if (hadDependencies)
didChangeDependencies
();
} }
/// Transition from the "active" to the "inactive" lifecycle state. /// Transition from the "active" to the "inactive" lifecycle state.
...@@ -2587,9 +2592,9 @@ abstract class Element implements BuildContext { ...@@ -2587,9 +2592,9 @@ abstract class Element implements BuildContext {
// For expediency, we don't actually clear the list here, even though it's // For expediency, we don't actually clear the list here, even though it's
// no longer representative of what we are registered with. If we never // no longer representative of what we are registered with. If we never
// get re-used, it doesn't matter. If we do, then we'll clear the list in // get re-used, it doesn't matter. If we do, then we'll clear the list in
// activate(). The benefit of this is that it allows BuildableElement's // activate(). The benefit of this is that it allows Element's activate()
// activate() implementation to decide whether to rebuild based on whether // implementation to decide whether to rebuild based on whether we had
// we had dependencies here. // dependencies here.
} }
_inheritedWidgets = null; _inheritedWidgets = null;
_active = false; _active = false;
...@@ -2787,7 +2792,10 @@ abstract class Element implements BuildContext { ...@@ -2787,7 +2792,10 @@ abstract class Element implements BuildContext {
/// [InheritedWidget.updateShouldNotify] returned true), the framework calls /// [InheritedWidget.updateShouldNotify] returned true), the framework calls
/// this function to notify this element of the change. /// this function to notify this element of the change.
@mustCallSuper @mustCallSuper
void didChangeDependencies() { } void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
markNeedsBuild();
}
/// Returns a description of what caused this element to be created. /// Returns a description of what caused this element to be created.
/// ///
...@@ -2838,6 +2846,8 @@ abstract class Element implements BuildContext { ...@@ -2838,6 +2846,8 @@ abstract class Element implements BuildContext {
description.add('${widget.key}'); description.add('${widget.key}');
widget.debugFillDescription(description); widget.debugFillDescription(description);
} }
if (dirty)
description.add('dirty');
} }
/// A detailed, textual description of this element, includings its children. /// A detailed, textual description of this element, includings its children.
...@@ -2853,50 +2863,8 @@ abstract class Element implements BuildContext { ...@@ -2853,50 +2863,8 @@ abstract class Element implements BuildContext {
} }
return result; return result;
} }
}
/// A widget that renders an exception's message.
///
/// This widget is used when a build function fails, to help with determining
/// where the problem lies. Exceptions are also logged to the console, which you
/// can read using `flutter logs`. The console will also include additional
/// information such as the stack trace for the exception.
class ErrorWidget extends LeafRenderObjectWidget {
/// Creates a widget that displays the given error message.
ErrorWidget(Object exception) : message = _stringify(exception),
super(key: new UniqueKey());
/// The message to display.
final String message;
static String _stringify(Object exception) {
try {
return exception.toString();
} catch (e) { }
return 'Error';
}
@override
RenderBox createRenderObject(BuildContext context) => new RenderErrorBox(message);
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('message: ' + _stringify(message));
}
}
/// An [Element] that can be marked dirty and rebuilt.
///
/// In practice, all subclasses of [Element] in the Flutter framework are
/// subclasses of [BuildableElement]. The distinction exists primarily to
/// segregate unrelated code.
abstract class BuildableElement extends Element {
/// Creates an element that uses the given widget as its configuration.
BuildableElement(Widget widget) : super(widget);
/// Returns true if the element has been marked as needing rebuilding. /// Returns true if the element has been marked as needing rebuilding.
@override
bool get dirty => _dirty; bool get dirty => _dirty;
bool _dirty = true; bool _dirty = true;
...@@ -2991,7 +2959,7 @@ abstract class BuildableElement extends Element { ...@@ -2991,7 +2959,7 @@ abstract class BuildableElement extends Element {
}); });
assert(_debugLifecycleState == _ElementLifecycle.active); assert(_debugLifecycleState == _ElementLifecycle.active);
assert(owner._debugStateLocked); assert(owner._debugStateLocked);
BuildableElement debugPreviousBuildTarget; Element debugPreviousBuildTarget;
assert(() { assert(() {
debugPreviousBuildTarget = owner._debugCurrentBuildTarget; debugPreviousBuildTarget = owner._debugCurrentBuildTarget;
owner._debugCurrentBuildTarget = this; owner._debugCurrentBuildTarget = this;
...@@ -3009,35 +2977,36 @@ abstract class BuildableElement extends Element { ...@@ -3009,35 +2977,36 @@ abstract class BuildableElement extends Element {
/// Called by rebuild() after the appropriate checks have been made. /// Called by rebuild() after the appropriate checks have been made.
@protected @protected
void performRebuild(); void performRebuild();
}
@override /// A widget that renders an exception's message.
void didChangeDependencies() { ///
super.didChangeDependencies(); /// This widget is used when a build function fails, to help with determining
assert(_active); // otherwise markNeedsBuild is a no-op /// where the problem lies. Exceptions are also logged to the console, which you
markNeedsBuild(); /// can read using `flutter logs`. The console will also include additional
} /// information such as the stack trace for the exception.
class ErrorWidget extends LeafRenderObjectWidget {
/// Creates a widget that displays the given error message.
ErrorWidget(Object exception) : message = _stringify(exception),
super(key: new UniqueKey());
@override /// The message to display.
void activate() { final String message;
final bool hadDependencies = ((_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies);
super.activate(); // clears _dependencies, and sets active to true static String _stringify(Object exception) {
if (_dirty) try {
owner.scheduleBuildFor(this); return exception.toString();
if (hadDependencies) } catch (e) { }
didChangeDependencies(); return 'Error';
} }
@override @override
void _reassemble() { RenderBox createRenderObject(BuildContext context) => new RenderErrorBox(message);
markNeedsBuild();
super._reassemble();
}
@override @override
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
if (dirty) description.add('message: ' + _stringify(message));
description.add('dirty');
} }
} }
...@@ -3059,7 +3028,7 @@ typedef Widget IndexedWidgetBuilder(BuildContext context, int index); ...@@ -3059,7 +3028,7 @@ typedef Widget IndexedWidgetBuilder(BuildContext context, int index);
/// [RenderObject]s indirectly by creating other [Element]s. /// [RenderObject]s indirectly by creating other [Element]s.
/// ///
/// Contrast with [RenderObjectElement]. /// Contrast with [RenderObjectElement].
abstract class ComponentElement extends BuildableElement { abstract class ComponentElement extends Element {
/// Creates an element that uses the given widget as its configuration. /// Creates an element that uses the given widget as its configuration.
ComponentElement(Widget widget) : super(widget); ComponentElement(Widget widget) : super(widget);
...@@ -3630,7 +3599,7 @@ class InheritedElement extends ProxyElement { ...@@ -3630,7 +3599,7 @@ class InheritedElement extends ProxyElement {
/// expose them in its implementation of the [visitChildren] method. This method /// expose them in its implementation of the [visitChildren] method. This method
/// is used by many of the framework's internal mechanisms, and so should be /// is used by many of the framework's internal mechanisms, and so should be
/// fast. It is also used by the test framework and [debugDumpApp]. /// fast. It is also used by the test framework and [debugDumpApp].
abstract class RenderObjectElement extends BuildableElement { abstract class RenderObjectElement extends Element {
/// Creates an element that uses the given widget as its configuration. /// Creates an element that uses the given widget as its configuration.
RenderObjectElement(RenderObjectWidget widget) : super(widget); RenderObjectElement(RenderObjectWidget widget) : super(widget);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment