Make debugCheckHasMaterial more useful (#14101)
Fixes https://github.com/flutter/flutter/issues/2877
diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart
index fece354..8b21778 100644
--- a/packages/flutter/lib/src/material/debug.dart
+++ b/packages/flutter/lib/src/material/debug.dart
@@ -22,20 +22,45 @@
bool debugCheckHasMaterial(BuildContext context) {
assert(() {
if (context.widget is! Material && context.ancestorWidgetOfExactType(Material) == null) {
- final Element element = context;
- throw new FlutterError(
- 'No Material widget found.\n'
- '${context.widget.runtimeType} widgets require a Material widget ancestor.\n'
- 'In material design, most widgets are conceptually "printed" on a sheet of material. In Flutter\'s material library, '
- 'that material is represented by the Material widget. It is the Material widget that renders ink splashes, for instance. '
- 'Because of this, many material library widgets require that there be a Material widget in the tree above them.\n'
- 'To introduce a Material widget, you can either directly include one, or use a widget that contains Material itself, '
- 'such as a Card, Dialog, Drawer, or Scaffold.\n'
- 'The specific widget that could not find a Material ancestor was:\n'
- ' ${context.widget}\n'
- 'The ownership chain for the affected widget is:\n'
- ' ${element.debugGetCreatorChain(10)}'
+ final StringBuffer message = new StringBuffer();
+ message.writeln('No Material widget found.');
+ message.writeln(
+ '${context.widget.runtimeType} widgets require a Material '
+ 'widget ancestor.'
);
+ message.writeln(
+ 'In material design, most widgets are conceptually "printed" on '
+ 'a sheet of material. In Flutter\'s material library, that '
+ 'material is represented by the Material widget. It is the '
+ 'Material widget that renders ink splashes, for instance. '
+ 'Because of this, many material library widgets require that '
+ 'there be a Material widget in the tree above them.'
+ );
+ message.writeln(
+ 'To introduce a Material widget, you can either directly '
+ 'include one, or use a widget that contains Material itself, '
+ 'such as a Card, Dialog, Drawer, or Scaffold.'
+ );
+ message.writeln(
+ 'The specific widget that could not find a Material ancestor was:'
+ );
+ message.writeln(' ${context.widget}');
+ final List<Widget> ancestors = <Widget>[];
+ context.visitAncestorElements((Element element) {
+ ancestors.add(element.widget);
+ return true;
+ });
+ if (ancestors.isNotEmpty) {
+ message.write('The ancestors of this widget were:');
+ for (Widget ancestor in ancestors)
+ message.write('\n $ancestor');
+ } else {
+ message.writeln(
+ 'This widget is the root of the tree, so it has no '
+ 'ancestors, let alone a "Material" ancestor.'
+ );
+ }
+ throw new FlutterError(message.toString());
}
return true;
}());
diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart
index 46da6b0..f347904 100644
--- a/packages/flutter/test/material/debug_test.dart
+++ b/packages/flutter/test/material/debug_test.dart
@@ -11,6 +11,8 @@
onPressed: null,
child: const Text('Go'),
));
- expect(tester.takeException(), isFlutterError);
+ final dynamic exception = tester.takeException();
+ expect(exception, isFlutterError);
+ expect(exception.toString(), endsWith(':\n FlatButton(disabled)\n [root]'));
});
}