diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index bab4db64a63..464baa44238 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -110,6 +110,11 @@ class StockHomeState extends State { icon: 'action/account_balance', child: new Text('Account Balance') ), + new DrawerItem( + icon: 'device/dvr', + onPressed: () { debugDumpApp(); }, + child: new Text('Dump App to Console') + ), new DrawerDivider(), new DrawerItem( icon: 'action/thumb_up', diff --git a/packages/flutter/lib/src/fn3/binding.dart b/packages/flutter/lib/src/fn3/binding.dart index 00ebaed5fc2..3ae867163d1 100644 --- a/packages/flutter/lib/src/fn3/binding.dart +++ b/packages/flutter/lib/src/fn3/binding.dart @@ -81,6 +81,12 @@ void runApp(Widget app) { }); } +void debugDumpApp() { + assert(WidgetFlutterBinding.instance != null); + assert(WidgetFlutterBinding.instance.renderViewElement != null); + WidgetFlutterBinding.instance.renderViewElement.toStringDeep().split('\n').forEach(print); +} + /// This class provides a bridge from a RenderObject to an Element tree. The /// given container is the RenderObject that the Element tree should be inserted /// into. It must be a RenderObject that implements the diff --git a/packages/flutter/lib/src/fn3/framework.dart b/packages/flutter/lib/src/fn3/framework.dart index ba0f5d77d81..e391f35a3f2 100644 --- a/packages/flutter/lib/src/fn3/framework.dart +++ b/packages/flutter/lib/src/fn3/framework.dart @@ -177,6 +177,8 @@ abstract class Widget { /// Inflates this configuration to a concrete instance. Element createElement(); + + String toString() => '$runtimeType'; } /// RenderObjectWidgets provide the configuration for [RenderObjectElement]s, @@ -346,6 +348,22 @@ abstract class State { /// the tree at which this component is being built. For example, the context /// provides the set of inherited widgets for this location in the tree. Widget build(BuildContext context); + + String toString() { + final List data = []; + debugFillDescription(data); + return '$runtimeType(${data.join("; ")})'; + } + + void debugFillDescription(List description) { + description.add('$hashCode'); + if (_debugLifecycleState != _StateLifecycle.ready) + description.add('$_debugLifecycleState'); + if (_config == null) + description.add('no config'); + if (_element == null) + description.add('not mounted'); + } } abstract class ProxyWidget extends StatelessComponent { @@ -618,6 +636,35 @@ abstract class Element implements BuildContext { void dependenciesChanged() { assert(false); } + + String toString() { + final List data = []; + debugFillDescription(data); + final String name = widget != null ? '$widget' : '[$runtimeType]'; + return '$name(${data.join("; ")})'; + } + + void debugFillDescription(List description) { + if (depth == null) + description.add('no depth'); + if (widget == null) + description.add('no widget'); + } + + String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) { + String result = '${prefixLineOne}$this\n'; + List children = []; + visitChildren((Element child) { + children.add(child); + }); + if (children.length > 0) { + Element last = children.removeLast(); + for (Element child in children) + result += '${child.toStringDeep("$prefixOtherLines \u251C", "$prefixOtherLines \u2502")}'; + result += '${last.toStringDeep("$prefixOtherLines \u2514", "$prefixOtherLines ")}'; + } + return result; + } } class ErrorWidget extends LeafRenderObjectWidget { @@ -666,7 +713,7 @@ abstract class BuildableElement extends Element { /// and by update() when the Widget has changed. void rebuild() { assert(_debugLifecycleState != _ElementLifecycle.initial); - if (!_dirty) + if (!dirty) return; assert(_debugLifecycleState == _ElementLifecycle.mounted); assert(_debugStateLocked); @@ -728,8 +775,8 @@ abstract class BuildableElement extends Element { /// the build itself. void markNeedsBuild() { assert(_debugLifecycleState != _ElementLifecycle.defunct); - assert(!_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && _dirty)); - if (_dirty) + assert(!_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && dirty)); + if (dirty) return; assert(_debugLifecycleState == _ElementLifecycle.mounted); assert(!_debugStateLocked); @@ -751,6 +798,12 @@ abstract class BuildableElement extends Element { void dependenciesChanged() { markNeedsBuild(); } + + void debugFillDescription(List description) { + super.debugFillDescription(description); + if (dirty) + description.add('dirty'); + } } /// Instantiation of StatelessComponent widgets. @@ -824,10 +877,16 @@ class StatefulComponentElement extends BuildableElement { print('${_state.runtimeType}.dispose failed to call super.dispose'); return false; }); - assert(!_dirty); // See BuildableElement.unmount for why this is important. + assert(!dirty); // See BuildableElement.unmount for why this is important. _state._element = null; _state = null; } + + void debugFillDescription(List description) { + super.debugFillDescription(description); + if (state != null) + description.add('state: $state'); + } } class ParentDataElement extends StatelessComponentElement { @@ -1112,6 +1171,12 @@ abstract class RenderObjectElement extends Element void insertChildRenderObject(RenderObject child, dynamic slot); void moveChildRenderObject(RenderObject child, dynamic slot); void removeChildRenderObject(RenderObject child); + + void debugFillDescription(List description) { + super.debugFillDescription(description); + if (renderObject != null) + description.add('renderObject: $renderObject'); + } } /// Instantiation of RenderObjectWidgets that have no children