Better error message when TextField is not in a Material (#16147)

Added a call to debugCheckHasMaterial() at the start of TextField's build method.
diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart
index 5d37747..7e7dab3 100644
--- a/packages/flutter/lib/src/material/text_field.dart
+++ b/packages/flutter/lib/src/material/text_field.dart
@@ -10,6 +10,7 @@
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 
+import 'debug.dart';
 import 'feedback.dart';
 import 'ink_well.dart' show InteractiveInkFeature;
 import 'input_decorator.dart';
@@ -42,7 +43,9 @@
 /// extra padding introduced by the decoration to save space for the labels.
 ///
 /// If [decoration] is non-null (which is the default), the text field requires
-/// one of its ancestors to be a [Material] widget.
+/// one of its ancestors to be a [Material] widget. When the [TextField] is
+/// tapped an ink splash that paints on the material is triggered, see
+/// [ThemeData.splashFactory].
 ///
 /// To integrate the [TextField] into a [Form] with other [FormField] widgets,
 /// consider using [TextFormField].
@@ -450,6 +453,7 @@
   @override
   Widget build(BuildContext context) {
     super.build(context); // See AutomaticKeepAliveClientMixin.
+    assert(debugCheckHasMaterial(context));
     final ThemeData themeData = Theme.of(context);
     final TextStyle style = widget.style ?? themeData.textTheme.subhead;
     final TextEditingController controller = _effectiveController;
diff --git a/packages/flutter/test/cupertino/tab_scaffold_test.dart b/packages/flutter/test/cupertino/tab_scaffold_test.dart
index f9f70ff..7e62b4c 100644
--- a/packages/flutter/test/cupertino/tab_scaffold_test.dart
+++ b/packages/flutter/test/cupertino/tab_scaffold_test.dart
@@ -132,14 +132,16 @@
           return new CupertinoPageRoute<void>(
             settings: settings,
             builder: (BuildContext context) {
-              return new CupertinoTabScaffold(
-                tabBar: _buildTabBar(),
-                tabBuilder: (BuildContext context, int index) {
-                  return new TextField(
-                    focusNode: focusNodes[index],
-                    autofocus: true,
-                  );
-                },
+              return new Material(
+                child: new CupertinoTabScaffold(
+                  tabBar: _buildTabBar(),
+                  tabBuilder: (BuildContext context, int index) {
+                    return new TextField(
+                      focusNode: focusNodes[index],
+                      autofocus: true,
+                    );
+                  },
+                ),
               );
             },
           );
diff --git a/packages/flutter/test/material/text_field_helper_text_test.dart b/packages/flutter/test/material/text_field_helper_text_test.dart
index 44514eb..1800bf5 100644
--- a/packages/flutter/test/material/text_field_helper_text_test.dart
+++ b/packages/flutter/test/material/text_field_helper_text_test.dart
@@ -7,11 +7,11 @@
 
 void main() {
   testWidgets('TextField works correctly when changing helperText', (WidgetTester tester) async {
-    await tester.pumpWidget(new MaterialApp(home: const TextField(decoration: const InputDecoration(helperText: 'Awesome'))));
+    await tester.pumpWidget(new MaterialApp(home: const Material(child: const TextField(decoration: const InputDecoration(helperText: 'Awesome')))));
     expect(find.text('Awesome'), findsNWidgets(1));
     await tester.pump(const Duration(milliseconds: 100));
     expect(find.text('Awesome'), findsNWidgets(1));
-    await tester.pumpWidget(new MaterialApp(home: const TextField(decoration: const InputDecoration(errorText: 'Awesome'))));
+    await tester.pumpWidget(new MaterialApp(home: const Material(child: const TextField(decoration: const InputDecoration(errorText: 'Awesome')))));
     expect(find.text('Awesome'), findsNWidgets(2));
   });
 }
\ No newline at end of file
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 8e7ea20..926fdd3 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -2060,5 +2060,13 @@
     semantics.dispose();
   });
 
+  testWidgets('TextField throws when not descended from a Material widget', (WidgetTester tester) async {
+    const Widget textField = const TextField();
+    await tester.pumpWidget(textField);
+    final dynamic exception = tester.takeException();
+    expect(exception, isFlutterError);
+    expect(exception.toString(), startsWith('No Material widget found.'));
+    expect(exception.toString(), endsWith(':\n  $textField\nThe ancestors of this widget were:\n  [root]'));
+  });
 
 }
diff --git a/packages/flutter/test/widgets/physical_model_test.dart b/packages/flutter/test/widgets/physical_model_test.dart
index f575d06..7578ac7 100644
--- a/packages/flutter/test/widgets/physical_model_test.dart
+++ b/packages/flutter/test/widgets/physical_model_test.dart
@@ -6,15 +6,16 @@
 void main() {
   testWidgets('PhysicalModel - creates a physical model layer when it needs compositing', (WidgetTester tester) async {
     await tester.pumpWidget(new Directionality(
-      textDirection: TextDirection.ltr,
-      child: new PhysicalModel(
-        shape: BoxShape.rectangle,
-        color: Colors.grey,
-        shadowColor: Colors.red,
-        elevation: 1.0,
-        child: new TextField(controller: new TextEditingController()),
+        textDirection: TextDirection.ltr,
+        child: new PhysicalModel(
+          shape: BoxShape.rectangle,
+          color: Colors.grey,
+          shadowColor: Colors.red,
+          elevation: 1.0,
+          child: new Material(child: new TextField(controller: new TextEditingController())),
+        ),
       ),
-    ));
+    );
     await tester.pump();
 
     final RenderPhysicalModel renderPhysicalModel = tester.allRenderObjects.firstWhere((RenderObject object) => object is RenderPhysicalModel);