Merge pull request #812 from abarth/check_material
Widgets that depend on Material should assert that
diff --git a/packages/flutter/lib/src/material/checkbox.dart b/packages/flutter/lib/src/material/checkbox.dart
index 0cb5a56..55d8a8f 100644
--- a/packages/flutter/lib/src/material/checkbox.dart
+++ b/packages/flutter/lib/src/material/checkbox.dart
@@ -8,6 +8,7 @@
import 'package:flutter/widgets.dart';
import 'constants.dart';
+import 'debug.dart';
import 'theme.dart';
import 'toggleable.dart';
@@ -35,6 +36,7 @@
final ValueChanged<bool> onChanged;
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
return new _CheckboxRenderObjectWidget(
value: value,
diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart
index 73aa1b7..33ebb7d 100644
--- a/packages/flutter/lib/src/material/chip.dart
+++ b/packages/flutter/lib/src/material/chip.dart
@@ -5,6 +5,7 @@
import 'package:flutter/widgets.dart';
import 'colors.dart';
+import 'debug.dart';
import 'icon.dart';
const double _kChipHeight = 32.0;
@@ -34,6 +35,7 @@
final VoidCallback onDeleted;
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
final bool deletable = onDeleted != null;
double leftPadding = 12.0;
double rightPadding = 12.0;
diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart
new file mode 100644
index 0000000..086a566
--- /dev/null
+++ b/packages/flutter/lib/src/material/debug.dart
@@ -0,0 +1,18 @@
+// Copyright 2015 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/widgets.dart';
+
+import 'material.dart';
+
+bool debugCheckHasMaterial(BuildContext context) {
+ assert(() {
+ if (context.widget is Material || context.ancestorWidgetOfType(Material) != null)
+ return true;
+ Element element = context;
+ debugPrint('${context.widget} needs to be placed inside a Material widget. Ownership chain:\n${element.debugGetOwnershipChain(10)}');
+ return false;
+ });
+ return true;
+}
diff --git a/packages/flutter/lib/src/material/drawer_header.dart b/packages/flutter/lib/src/material/drawer_header.dart
index 650d0f2..a004bf2 100644
--- a/packages/flutter/lib/src/material/drawer_header.dart
+++ b/packages/flutter/lib/src/material/drawer_header.dart
@@ -5,6 +5,7 @@
import 'package:flutter/widgets.dart';
import 'constants.dart';
+import 'debug.dart';
import 'theme.dart';
// TODO(jackson): This class should usually render the user's
@@ -16,6 +17,7 @@
final Widget child;
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
return new Container(
height: kStatusBarHeight + kMaterialDrawerHeight,
decoration: new BoxDecoration(
diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart
index c094cf7..b4984e0 100644
--- a/packages/flutter/lib/src/material/dropdown.dart
+++ b/packages/flutter/lib/src/material/dropdown.dart
@@ -9,6 +9,7 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'debug.dart';
import 'icon.dart';
import 'ink_well.dart';
import 'shadows.dart';
@@ -262,6 +263,7 @@
}
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
return new GestureDetector(
onTap: _handleTap,
child: new Container(
diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart
index 9e3419f..a70d53f 100644
--- a/packages/flutter/lib/src/material/ink_well.dart
+++ b/packages/flutter/lib/src/material/ink_well.dart
@@ -6,6 +6,7 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'debug.dart';
import 'material.dart';
import 'theme.dart';
@@ -137,6 +138,7 @@
}
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
final bool enabled = config.onTap != null || config.onDoubleTap != null || config.onLongPress != null;
return new GestureDetector(
onTapDown: enabled ? _handleTapDown : null,
diff --git a/packages/flutter/lib/src/material/input.dart b/packages/flutter/lib/src/material/input.dart
index 5b904f9..f8f2aa7 100644
--- a/packages/flutter/lib/src/material/input.dart
+++ b/packages/flutter/lib/src/material/input.dart
@@ -7,6 +7,7 @@
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
+import 'debug.dart';
import 'theme.dart';
export 'package:flutter/rendering.dart' show ValueChanged;
@@ -75,6 +76,7 @@
}
Widget buildContent(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
bool focused = Focus.at(context, config);
diff --git a/packages/flutter/lib/src/material/material_button.dart b/packages/flutter/lib/src/material/material_button.dart
index 9503752..cfc825c 100644
--- a/packages/flutter/lib/src/material/material_button.dart
+++ b/packages/flutter/lib/src/material/material_button.dart
@@ -5,6 +5,7 @@
import 'package:flutter/widgets.dart';
import 'colors.dart';
+import 'debug.dart';
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
@@ -98,6 +99,7 @@
}
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
Widget contents = new InkWell(
onTap: config.onPressed,
onHighlightChanged: _handleHighlightChanged,
diff --git a/packages/flutter/lib/src/material/radio.dart b/packages/flutter/lib/src/material/radio.dart
index b9866a9..1983dd7 100644
--- a/packages/flutter/lib/src/material/radio.dart
+++ b/packages/flutter/lib/src/material/radio.dart
@@ -8,6 +8,7 @@
import 'package:flutter/widgets.dart';
import 'constants.dart';
+import 'debug.dart';
import 'theme.dart';
import 'toggleable.dart';
@@ -39,6 +40,7 @@
}
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
return new _RadioRenderObjectWidget(
selected: value == groupValue,
diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart
index 08c4e36..a94e26d 100644
--- a/packages/flutter/lib/src/material/slider.dart
+++ b/packages/flutter/lib/src/material/slider.dart
@@ -10,6 +10,7 @@
import 'colors.dart';
import 'constants.dart';
+import 'debug.dart';
import 'theme.dart';
class Slider extends StatelessComponent {
@@ -37,6 +38,7 @@
}
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
return new _SliderRenderObjectWidget(
value: (value - min) / (max - min),
primaryColor: Theme.of(context).accentColor,
diff --git a/packages/flutter/lib/src/material/switch.dart b/packages/flutter/lib/src/material/switch.dart
index 8ced196..dc2577f 100644
--- a/packages/flutter/lib/src/material/switch.dart
+++ b/packages/flutter/lib/src/material/switch.dart
@@ -11,6 +11,7 @@
import 'colors.dart';
import 'constants.dart';
+import 'debug.dart';
import 'shadows.dart';
import 'theme.dart';
import 'toggleable.dart';
@@ -23,6 +24,7 @@
final ValueChanged<bool> onChanged;
Widget build(BuildContext context) {
+ assert(debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
final isDark = themeData.brightness == ThemeBrightness.dark;
diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart
index 71e67b1..48c4c7a 100644
--- a/packages/flutter/lib/src/widgets/binding.dart
+++ b/packages/flutter/lib/src/widgets/binding.dart
@@ -76,6 +76,7 @@
void _runApp(Widget app) {
_renderViewElement = new RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
+ debugShortDescription: '[root]',
child: app
).attachToRenderTree(_renderViewElement);
beginFrame();
@@ -102,11 +103,15 @@
/// RenderObjectWithChildMixin protocol. The type argument T is the kind of
/// RenderObject that the container expects as its child.
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
- RenderObjectToWidgetAdapter({ this.child, RenderObjectWithChildMixin<T> container })
- : container = container, super(key: new GlobalObjectKey(container));
+ RenderObjectToWidgetAdapter({
+ this.child,
+ RenderObjectWithChildMixin<T> container,
+ this.debugShortDescription
+ }) : container = container, super(key: new GlobalObjectKey(container));
final Widget child;
final RenderObjectWithChildMixin<T> container;
+ final String debugShortDescription;
RenderObjectToWidgetElement<T> createElement() => new RenderObjectToWidgetElement<T>(this);
@@ -125,6 +130,8 @@
}, building: true);
return element;
}
+
+ String toStringShort() => debugShortDescription ?? super.toStringShort();
}
/// This element class is the instantiation of a [RenderObjectToWidgetAdapter].
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index 69f7677..81eeb12 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -875,6 +875,18 @@
assert(false);
}
+ String debugGetOwnershipChain(int limit) {
+ List<String> chain = <String>[];
+ Element node = this;
+ while (chain.length < limit && node != null) {
+ chain.add(node.toStringShort());
+ node = node._parent;
+ }
+ if (node != null)
+ chain.add('\u22EF');
+ return chain.join(' \u2190 ');
+ }
+
String toStringShort() {
return widget != null ? '${widget.toStringShort()}' : '[$runtimeType]';
}
@@ -1354,15 +1366,7 @@
}
void debugUpdateRenderObjectOwner() {
- List<String> chain = <String>[];
- Element node = this;
- while (chain.length < 4 && node != null) {
- chain.add(node.toStringShort());
- node = node._parent;
- }
- if (node != null)
- chain.add('\u22EF');
- _renderObject.debugOwner = chain.join(' \u2190 ');
+ _renderObject.debugOwner = debugGetOwnershipChain(4);
}
void performRebuild() {
diff --git a/packages/unit/test/widget/input_test.dart b/packages/unit/test/widget/input_test.dart
index 3dedf70..2e9c21b 100644
--- a/packages/unit/test/widget/input_test.dart
+++ b/packages/unit/test/widget/input_test.dart
@@ -38,10 +38,12 @@
Widget builder() {
return new Center(
- child: new Input(
- key: inputKey,
- placeholder: 'Placeholder',
- onChanged: (String value) { inputValue = value; }
+ child: new Material(
+ child: new Input(
+ key: inputKey,
+ placeholder: 'Placeholder',
+ onChanged: (String value) { inputValue = value; }
+ )
)
);
}
@@ -72,9 +74,11 @@
Widget builder() {
return new Center(
- child: new Input(
- key: inputKey,
- placeholder: 'Placeholder'
+ child: new Material(
+ child: new Input(
+ key: inputKey,
+ placeholder: 'Placeholder'
+ )
)
);
}
@@ -112,9 +116,11 @@
Widget builder() {
return new Center(
- child: new Input(
- key: inputKey,
- placeholder: 'Placeholder'
+ child: new Material(
+ child: new Input(
+ key: inputKey,
+ placeholder: 'Placeholder'
+ )
)
);
}
@@ -145,10 +151,12 @@
Widget builder() {
return new Center(
- child: new Input(
- key: inputKey,
- hideText: true,
- placeholder: 'Placeholder'
+ child: new Material(
+ child: new Input(
+ key: inputKey,
+ hideText: true,
+ placeholder: 'Placeholder'
+ )
)
);
}