Revert "Re-land ScaffoldMessenger (#65416)" (#65482)

This reverts commit adc5f26b504f3cb80557455d8f0badad874ebc8b.
diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart
index cb832ae..a1c7851 100644
--- a/packages/flutter/lib/src/material/app.dart
+++ b/packages/flutter/lib/src/material/app.dart
@@ -17,7 +17,6 @@
 import 'icons.dart';
 import 'material_localizations.dart';
 import 'page.dart';
-import 'scaffold.dart';
 import 'theme.dart';
 
 /// [MaterialApp] uses this [TextStyle] as its [DefaultTextStyle] to encourage
@@ -169,7 +168,6 @@
   const MaterialApp({
     Key key,
     this.navigatorKey,
-    this.scaffoldMessengerKey,
     this.home,
     this.routes = const <String, WidgetBuilder>{},
     this.initialRoute,
@@ -217,7 +215,6 @@
   /// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator].
   const MaterialApp.router({
     Key key,
-    this.scaffoldMessengerKey,
     this.routeInformationProvider,
     @required this.routeInformationParser,
     @required this.routerDelegate,
@@ -266,14 +263,6 @@
   /// {@macro flutter.widgets.widgetsApp.navigatorKey}
   final GlobalKey<NavigatorState> navigatorKey;
 
-  /// A key to use when building the [ScaffoldMessenger].
-  ///
-  /// If a [scaffoldMessengerKey] is specified, the [ScaffoldMessenger] can be
-  /// directly manipulated without first obtaining it from a [BuildContext] via
-  /// [ScaffoldMessenger.of]: from the [scaffoldMessengerKey], use the
-  /// [GlobalKey.currentState] getter.
-  final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey;
-
   /// {@macro flutter.widgets.widgetsApp.home}
   final Widget home;
 
@@ -733,30 +722,27 @@
     }
     theme ??= widget.theme ?? ThemeData.light();
 
-    return ScaffoldMessenger(
-      key: widget.scaffoldMessengerKey,
-      child: AnimatedTheme(
-        data: theme,
-        isMaterialAppTheme: true,
-        child: widget.builder != null
-          ? Builder(
-              builder: (BuildContext context) {
-                // Why are we surrounding a builder with a builder?
-                //
-                // The widget.builder may contain code that invokes
-                // Theme.of(), which should return the theme we selected
-                // above in AnimatedTheme. However, if we invoke
-                // widget.builder() directly as the child of AnimatedTheme
-                // then there is no Context separating them, and the
-                // widget.builder() will not find the theme. Therefore, we
-                // surround widget.builder with yet another builder so that
-                // a context separates them and Theme.of() correctly
-                // resolves to the theme we passed to AnimatedTheme.
-                return widget.builder(context, child);
-              },
-            )
-          : child,
-      )
+    return AnimatedTheme(
+      data: theme,
+      isMaterialAppTheme: true,
+      child: widget.builder != null
+        ? Builder(
+            builder: (BuildContext context) {
+              // Why are we surrounding a builder with a builder?
+              //
+              // The widget.builder may contain code that invokes
+              // Theme.of(), which should return the theme we selected
+              // above in AnimatedTheme. However, if we invoke
+              // widget.builder() directly as the child of AnimatedTheme
+              // then there is no Context separating them, and the
+              // widget.builder() will not find the theme. Therefore, we
+              // surround widget.builder with yet another builder so that
+              // a context separates them and Theme.of() correctly
+              // resolves to the theme we passed to AnimatedTheme.
+              return widget.builder(context, child);
+            },
+          )
+        : child,
     );
   }
 
diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart
index ec694b0..b198a80 100644
--- a/packages/flutter/lib/src/material/app_bar.dart
+++ b/packages/flutter/lib/src/material/app_bar.dart
@@ -137,7 +137,7 @@
 ///           icon: const Icon(Icons.add_alert),
 ///           tooltip: 'Show Snackbar',
 ///           onPressed: () {
-///             ScaffoldMessenger.of(context).showSnackBar(snackBar);
+///             scaffoldKey.currentState.showSnackBar(snackBar);
 ///           },
 ///         ),
 ///         IconButton(
diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart
index edee89e..e90ee46 100644
--- a/packages/flutter/lib/src/material/debug.dart
+++ b/packages/flutter/lib/src/material/debug.dart
@@ -9,7 +9,7 @@
 
 import 'material.dart';
 import 'material_localizations.dart';
-import 'scaffold.dart' show Scaffold, ScaffoldMessenger;
+import 'scaffold.dart' show Scaffold;
 
 /// Asserts that the given context has a [Material] ancestor.
 ///
@@ -125,34 +125,3 @@
   }());
   return true;
 }
-
-/// Asserts that the given context has a [ScaffoldMessenger] ancestor.
-///
-/// Used by various widgets to make sure that they are only used in an
-/// appropriate context.
-///
-/// To invoke this function, use the following pattern, typically in the
-/// relevant Widget's build method:
-///
-/// ```dart
-/// assert(debugCheckHasScaffoldMessenger(context));
-/// ```
-///
-/// Does nothing if asserts are disabled. Always returns true.
-bool debugCheckHasScaffoldMessenger(BuildContext context) {
-  assert(() {
-    if (context.widget is! ScaffoldMessenger && context.findAncestorWidgetOfExactType<ScaffoldMessenger>() == null) {
-      throw FlutterError.fromParts(<DiagnosticsNode>[
-        ErrorSummary('No ScaffoldMessenger widget found.'),
-        ErrorDescription('${context.widget.runtimeType} widgets require a ScaffoldMessenger widget ancestor.'),
-        ...context.describeMissingAncestor(expectedAncestorType: ScaffoldMessenger),
-        ErrorHint(
-          'Typically, the ScaffoldMessenger widget is introduced by the MaterialApp '
-          'at the top of your application widget tree.'
-        )
-      ]);
-    }
-    return true;
-  }());
-  return true;
-}
diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart
index c435c9a..17b87fa 100644
--- a/packages/flutter/lib/src/material/scaffold.dart
+++ b/packages/flutter/lib/src/material/scaffold.dart
@@ -19,7 +19,6 @@
 import 'button_bar.dart';
 import 'colors.dart';
 import 'curves.dart';
-import 'debug.dart';
 import 'divider.dart';
 import 'drawer.dart';
 import 'flexible_space_bar.dart';
@@ -62,372 +61,6 @@
   statusBar,
 }
 
-/// Manages [SnackBar]s for descendant [Scaffold]s.
-///
-/// This class provides APIs for showing snack bars.
-///
-/// To display a snack bar, obtain the [ScaffoldMessengerState] for the current
-/// [BuildContext] via [ScaffoldMessenger.of] and use the
-/// [ScaffoldMessengerState.showSnackBar] function.
-///
-/// See also:
-///
-///  * [SnackBar], which is a temporary notification typically shown near the
-///    bottom of the app using the [ScaffoldMessengerState.showSnackBar] method.
-///  * Cookbook: [Display a snackbar](https://flutter.dev/docs/cookbook/design/snackbars)
-class ScaffoldMessenger extends StatefulWidget {
-  /// Creates a widget that manages [SnackBar]s for [Scaffold] descendants.
-  const ScaffoldMessenger({
-    Key key,
-    @required this.child,
-  }) : assert(child != null),
-       super(key: key);
-
-  /// The widget below this widget in the tree.
-  ///
-  /// {@macro flutter.widgets.child}
-  final Widget child;
-
-  /// The state from the closest instance of this class that encloses the given
-  /// context.
-  ///
-  /// {@tool dartpad --template=stateless_widget_scaffold_center}
-  /// Typical usage of the [ScaffoldMessenger.of] function is to call it in
-  /// response to a user gesture or an application state change.
-  ///
-  /// ```dart
-  /// Widget build(BuildContext context) {
-  ///   return ElevatedButton(
-  ///     child: const Text('SHOW A SNACKBAR'),
-  ///     onPressed: () {
-  ///       ScaffoldMessenger.of(context).showSnackBar(
-  ///         const SnackBar(
-  ///           content: Text('Have a snack!'),
-  ///         ),
-  ///       );
-  ///     },
-  ///   );
-  /// }
-  /// ```
-  /// {@end-tool}
-  ///
-  /// A less elegant but more expedient solution is assign a [GlobalKey] to the
-  /// [ScaffoldMessenger], then use the `key.currentState` property to obtain the
-  /// [ScaffoldMessengerState] rather than using the [ScaffoldMessenger.of]
-  /// function. The [MaterialApp.scaffoldMessengerKey] refers to the root
-  /// ScaffoldMessenger that is provided by default.
-  ///
-  /// {@tool dartpad --template=freeform}
-  /// Sometimes [SnackBar]s are produced by code that doesn't have ready access
-  /// to a valid [BuildContext]. One such example of this is when you may want
-  /// to show a SnackBar from a method outside of the `build` function. In these
-  /// cases, you can assign a [GlobalKey] to the [ScaffoldMessenger]. This
-  /// example shows a key being used to obtain the [ScaffoldMessengerState]
-  /// provided by the [MaterialApp].
-  ///
-  /// ```dart imports
-  /// import 'package:flutter/material.dart';
-  /// ```
-  /// ```dart
-  /// void main() => runApp(MyApp());
-  ///
-  /// class MyApp extends StatefulWidget {
-  ///   @override
-  ///  _MyAppState createState() => _MyAppState();
-  /// }
-  ///
-  /// class _MyAppState extends State<MyApp> {
-  ///   final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
-  ///   int _counter = 0;
-  ///
-  ///   void _incrementCounter() {
-  ///     setState(() {
-  ///       _counter++;
-  ///     });
-  ///     if (_counter % 10 == 0) {
-  ///       _scaffoldMessengerKey.currentState.showSnackBar(const SnackBar(
-  ///         content: Text('A multiple of ten!'),
-  ///       ));
-  ///     }
-  ///   }
-  ///
-  ///   @override
-  ///   Widget build(BuildContext context) {
-  ///     return MaterialApp(
-  ///       scaffoldMessengerKey: _scaffoldMessengerKey,
-  ///       home: Scaffold(
-  ///         appBar: AppBar(title: Text('ScaffoldMessenger Demo')),
-  ///         body: Center(
-  ///           child: Column(
-  ///             mainAxisAlignment: MainAxisAlignment.center,
-  ///             children: <Widget>[
-  ///               Text(
-  ///                 'You have pushed the button this many times:',
-  ///               ),
-  ///               Text(
-  ///                 '$_counter',
-  ///                 style: Theme.of(context).textTheme.headline4,
-  ///               ),
-  ///             ],
-  ///           ),
-  ///         ),
-  ///         floatingActionButton: FloatingActionButton(
-  ///           onPressed: _incrementCounter,
-  ///           tooltip: 'Increment',
-  ///           child: Icon(Icons.add),
-  ///         ),
-  ///       ),
-  ///     );
-  ///   }
-  /// }
-  ///
-  /// ```
-  /// {@end-tool}
-  ///
-  /// If there is no [ScaffoldMessenger] in scope, then this will throw an
-  /// exception.
-  static ScaffoldMessengerState of(BuildContext context) {
-    assert(context != null);
-    final _ScaffoldMessengerScope scope = context.dependOnInheritedWidgetOfExactType<_ScaffoldMessengerScope>();
-    return scope?._scaffoldMessengerState;
-  }
-
-  @override
-  ScaffoldMessengerState createState() => ScaffoldMessengerState();
-}
-
-/// State for a [ScaffoldMessenger].
-///
-/// A [ScaffoldMessengerState] object can be used to [showSnackBar] for every
-/// registered [Scaffold] that is a descendant of the associated
-/// [ScaffoldMessenger]. Scaffolds will register to receive [SnackBar]s from
-/// their closest ScaffoldMessenger ancestor.
-///
-/// Typically obtained via [ScaffoldMessenger.of].
-class ScaffoldMessengerState extends State<ScaffoldMessenger> with TickerProviderStateMixin {
-  final LinkedHashSet<ScaffoldState> _scaffolds = LinkedHashSet<ScaffoldState>();
-  final Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>> _snackBars = Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>>();
-  AnimationController _snackBarController;
-  Timer _snackBarTimer;
-  bool _accessibleNavigation;
-
-  @override
-  void didChangeDependencies() {
-    final MediaQueryData mediaQuery = MediaQuery.of(context);
-    // If we transition from accessible navigation to non-accessible navigation
-    // and there is a SnackBar that would have timed out that has already
-    // completed its timer, dismiss that SnackBar. If the timer hasn't finished
-    // yet, let it timeout as normal.
-    if (_accessibleNavigation == true
-        && !mediaQuery.accessibleNavigation
-        && _snackBarTimer != null
-        && !_snackBarTimer.isActive) {
-      hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
-    }
-    _accessibleNavigation = mediaQuery.accessibleNavigation;
-    super.didChangeDependencies();
-  }
-
-  void _register(ScaffoldState scaffold) {
-    _scaffolds.add(scaffold);
-    if (_snackBars.isNotEmpty) {
-      scaffold._updateSnackBar();
-    }
-  }
-
-  void _unregister(ScaffoldState scaffold) {
-    final bool removed = _scaffolds.remove(scaffold);
-    // ScaffoldStates should only be removed once.
-    assert(removed);
-  }
-
-  /// Shows  a [SnackBar] across all registered [Scaffold]s.
-  ///
-  /// A scaffold can show at most one snack bar at a time. If this function is
-  /// called while another snack bar is already visible, the given snack bar
-  /// will be added to a queue and displayed after the earlier snack bars have
-  /// closed.
-  ///
-  /// To control how long a [SnackBar] remains visible, use [SnackBar.duration].
-  ///
-  /// To remove the [SnackBar] with an exit animation, use [hideCurrentSnackBar]
-  /// or call [ScaffoldFeatureController.close] on the returned
-  /// [ScaffoldFeatureController]. To remove a [SnackBar] suddenly (without an
-  /// animation), use [removeCurrentSnackBar].
-  ///
-  /// See [ScaffoldMessenger.of] for information about how to obtain the
-  /// [ScaffoldMessengerState].
-  ///
-  /// {@tool dartpad --template=stateless_widget_scaffold_center}
-  ///
-  /// Here is an example of showing a [SnackBar] when the user presses a button.
-  ///
-  /// ```dart
-  ///   Widget build(BuildContext context) {
-  ///     return OutlinedButton(
-  ///       onPressed: () {
-  ///         ScaffoldMessenger.of(context).showSnackBar(
-  ///           const SnackBar(
-  ///             content: Text('A SnackBar has been shown.'),
-  ///           ),
-  ///         );
-  ///       },
-  ///       child: const Text('Show SnackBar'),
-  ///     );
-  ///   }
-  /// ```
-  /// {@end-tool}
-  ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(SnackBar snackBar) {
-    _snackBarController ??= SnackBar.createAnimationController(vsync: this)
-      ..addStatusListener(_handleStatusChanged);
-    if (_snackBars.isEmpty) {
-      assert(_snackBarController.isDismissed);
-      _snackBarController.forward();
-    }
-    ScaffoldFeatureController<SnackBar, SnackBarClosedReason> controller;
-    controller = ScaffoldFeatureController<SnackBar, SnackBarClosedReason>._(
-      // We provide a fallback key so that if back-to-back snackbars happen to
-      // match in structure, material ink splashes and highlights don't survive
-      // from one to the next.
-      snackBar.withAnimation(_snackBarController, fallbackKey: UniqueKey()),
-      Completer<SnackBarClosedReason>(),
-        () {
-        assert(_snackBars.first == controller);
-        hideCurrentSnackBar(reason: SnackBarClosedReason.hide);
-      },
-      null, // SnackBar doesn't use a builder function so setState() wouldn't rebuild it
-    );
-    setState(() {
-      _snackBars.addLast(controller);
-    });
-    _updateScaffolds();
-    return controller;
-  }
-
-  void _handleStatusChanged(AnimationStatus status) {
-    switch (status) {
-      case AnimationStatus.dismissed:
-        assert(_snackBars.isNotEmpty);
-        setState(() {
-          _snackBars.removeFirst();
-        });
-        _updateScaffolds();
-        if (_snackBars.isNotEmpty) {
-          _snackBarController.forward();
-        }
-        break;
-      case AnimationStatus.completed:
-        setState(() {
-          assert(_snackBarTimer == null);
-          // build will create a new timer if necessary to dismiss the snackBar.
-        });
-        _updateScaffolds();
-        break;
-      case AnimationStatus.forward:
-        break;
-      case AnimationStatus.reverse:
-        break;
-    }
-  }
-
-  void _updateScaffolds() {
-    for (final ScaffoldState scaffold in _scaffolds) {
-      scaffold._updateSnackBar();
-    }
-  }
-
-  /// Removes the current [SnackBar] (if any) immediately from registered
-  /// [Scaffold]s.
-  ///
-  /// The removed snack bar does not run its normal exit animation. If there are
-  /// any queued snack bars, they begin their entrance animation immediately.
-  void removeCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.remove }) {
-    assert(reason != null);
-    if (_snackBars.isEmpty)
-      return;
-    final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
-    if (!completer.isCompleted)
-      completer.complete(reason);
-    _snackBarTimer?.cancel();
-    _snackBarTimer = null;
-    // This will trigger the animation's status callback.
-    _snackBarController.value = 0.0;
-  }
-
-  /// Removes the current [SnackBar] by running its normal exit animation.
-  ///
-  /// The closed completer is called after the animation is complete.
-  void hideCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.hide }) {
-    assert(reason != null);
-    if (_snackBars.isEmpty || _snackBarController.status == AnimationStatus.dismissed)
-      return;
-    final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
-    if (_accessibleNavigation) {
-      _snackBarController.value = 0.0;
-      completer.complete(reason);
-    } else {
-      _snackBarController.reverse().then<void>((void value) {
-        assert(mounted);
-        if (!completer.isCompleted)
-          completer.complete(reason);
-      });
-    }
-    _snackBarTimer?.cancel();
-    _snackBarTimer = null;
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    assert(debugCheckHasMediaQuery(context));
-    final MediaQueryData mediaQuery = MediaQuery.of(context);
-    _accessibleNavigation = mediaQuery.accessibleNavigation;
-
-    if (_snackBars.isNotEmpty) {
-      final ModalRoute<dynamic> route = ModalRoute.of(context);
-      if (route == null || route.isCurrent) {
-        if (_snackBarController.isCompleted && _snackBarTimer == null) {
-          final SnackBar snackBar = _snackBars.first._widget;
-          _snackBarTimer = Timer(snackBar.duration, () {
-            assert(_snackBarController.status == AnimationStatus.forward || _snackBarController.status == AnimationStatus.completed);
-            // Look up MediaQuery again in case the setting changed.
-            final MediaQueryData mediaQuery = MediaQuery.of(context);
-            if (mediaQuery.accessibleNavigation && snackBar.action != null)
-              return;
-            hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
-          });
-        }
-      }
-    }
-
-    return _ScaffoldMessengerScope(
-      scaffoldMessengerState: this,
-      child: widget.child,
-    );
-  }
-
-  @override
-  void dispose() {
-    _snackBarController?.dispose();
-    _snackBarTimer?.cancel();
-    _snackBarTimer = null;
-    super.dispose();
-  }
-}
-
-class _ScaffoldMessengerScope extends InheritedWidget {
-  const _ScaffoldMessengerScope({
-    Key key,
-    Widget child,
-    ScaffoldMessengerState scaffoldMessengerState,
-  }) : _scaffoldMessengerState = scaffoldMessengerState,
-      super(key: key, child: child);
-
-  final ScaffoldMessengerState _scaffoldMessengerState;
-
-  @override
-  bool updateShouldNotify(_ScaffoldMessengerScope old) => _scaffoldMessengerState != old._scaffoldMessengerState;
-}
-
 /// The geometry of the [Scaffold] after all its contents have been laid out
 /// except the [FloatingActionButton].
 ///
@@ -1204,11 +837,11 @@
 
 /// Implements the basic material design visual layout structure.
 ///
-/// This class provides APIs for showing drawers and bottom sheets.
+/// This class provides APIs for showing drawers, snack bars, and bottom sheets.
 ///
-/// To display a persistent bottom sheet, obtain the
+/// To display a snackbar or a persistent bottom sheet, obtain the
 /// [ScaffoldState] for the current [BuildContext] via [Scaffold.of] and use the
-/// [ScaffoldState.showBottomSheet] function.
+/// [ScaffoldState.showSnackBar] and [ScaffoldState.showBottomSheet] functions.
 ///
 /// {@tool dartpad --template=stateful_widget_material}
 /// This example shows a [Scaffold] with a [body] and [FloatingActionButton].
@@ -1285,7 +918,7 @@
 /// Widget build(BuildContext context) {
 ///   return Scaffold(
 ///     appBar: AppBar(
-///       title: const Text('Sample Code'),
+///       title: Text('Sample Code'),
 ///     ),
 ///     body: Center(
 ///       child: Text('You have pressed the button $_count times.'),
@@ -1373,6 +1006,8 @@
 ///  * [BottomNavigationBar], which is a horizontal array of buttons typically
 ///    shown along the bottom of the app using the [bottomNavigationBar]
 ///    property.
+///  * [SnackBar], which is a temporary notification typically shown near the
+///    bottom of the app using the [ScaffoldState.showSnackBar] method.
 ///  * [BottomSheet], which is an overlay typically shown near the bottom of the
 ///    app. A bottom sheet can either be persistent, in which case it is shown
 ///    using the [ScaffoldState.showBottomSheet] method, or modal, in which case
@@ -1380,6 +1015,7 @@
 ///  * [ScaffoldState], which is the state associated with this widget.
 ///  * <https://material.io/design/layout/responsive-layout-grid.html>
 ///  * Cookbook: [Add a Drawer to a screen](https://flutter.dev/docs/cookbook/design/drawer)
+///  * Cookbook: [Display a snackbar](https://flutter.dev/docs/cookbook/design/snackbars)
 ///  * See our
 ///    [Scaffold Sample Apps](https://flutter.dev/docs/catalog/samples/Scaffold).
 class Scaffold extends StatefulWidget {
@@ -1751,7 +1387,7 @@
   ///       ),
   ///       home: Scaffold(
   ///         body: MyScaffoldBody(),
-  ///         appBar: AppBar(title: const Text('Scaffold.of Example')),
+  ///         appBar: AppBar(title: Text('Scaffold.of Example')),
   ///       ),
   ///       color: Colors.white,
   ///     );
@@ -1765,30 +1401,12 @@
   ///   Widget build(BuildContext context) {
   ///     return Center(
   ///       child: ElevatedButton(
-  ///         child: const Text('SHOW BOTTOM SHEET'),
+  ///         child: Text('SHOW A SNACKBAR'),
   ///         onPressed: () {
-  ///           Scaffold.of(context).showBottomSheet<void>(
-  ///             (BuildContext context) {
-  ///               return Container(
-  ///                 alignment: Alignment.center,
-  ///                 height: 200,
-  ///                 color: Colors.amber,
-  ///                 child: Center(
-  ///                   child: Column(
-  ///                     mainAxisSize: MainAxisSize.min,
-  ///                     children: <Widget>[
-  ///                       const Text('BottomSheet'),
-  ///                       ElevatedButton(
-  ///                         child: const Text('Close BottomSheet'),
-  ///                         onPressed: () {
-  ///                           Navigator.pop(context);
-  ///                         },
-  ///                       )
-  ///                     ],
-  ///                   ),
-  ///                 ),
-  ///               );
-  ///             },
+  ///           Scaffold.of(context).showSnackBar(
+  ///             SnackBar(
+  ///               content: Text('Have a snack!'),
+  ///             ),
   ///           );
   ///         },
   ///       ),
@@ -1809,38 +1427,20 @@
   /// ```dart
   /// Widget build(BuildContext context) {
   ///   return Scaffold(
-  ///     appBar: AppBar(title: const Text('Demo')),
+  ///     appBar: AppBar(
+  ///       title: Text('Demo')
+  ///     ),
   ///     body: Builder(
   ///       // Create an inner BuildContext so that the onPressed methods
   ///       // can refer to the Scaffold with Scaffold.of().
   ///       builder: (BuildContext context) {
   ///         return Center(
   ///           child: ElevatedButton(
-  ///             child: const Text('SHOW BOTTOM SHEET'),
+  ///             child: Text('SHOW A SNACKBAR'),
   ///             onPressed: () {
-  ///               Scaffold.of(context).showBottomSheet<void>(
-  ///                 (BuildContext context) {
-  ///                   return Container(
-  ///                     alignment: Alignment.center,
-  ///                     height: 200,
-  ///                     color: Colors.amber,
-  ///                     child: Center(
-  ///                       child: Column(
-  ///                         mainAxisSize: MainAxisSize.min,
-  ///                         children: <Widget>[
-  ///                           const Text('BottomSheet'),
-  ///                           ElevatedButton(
-  ///                             child: const Text('Close BottomSheet'),
-  ///                             onPressed: () {
-  ///                               Navigator.pop(context);
-  ///                             },
-  ///                           )
-  ///                         ],
-  ///                       ),
-  ///                     ),
-  ///                   );
-  ///                 },
-  ///               );
+  ///               Scaffold.of(context).showSnackBar(SnackBar(
+  ///                 content: Text('Have a snack!'),
+  ///               ));
   ///             },
   ///           ),
   ///         );
@@ -1957,7 +1557,7 @@
   /// See also:
   ///
   ///  * [Scaffold.of], which provides access to the [ScaffoldState] object as a
-  ///    whole, from which you can show bottom sheets, and so forth.
+  ///    whole, from which you can show snackbars, bottom sheets, and so forth.
   static bool hasDrawer(BuildContext context, { bool registerForUpdates = true }) {
     assert(registerForUpdates != null);
     assert(context != null);
@@ -1976,8 +1576,8 @@
 
 /// State for a [Scaffold].
 ///
-/// Can display [BottomSheet]s. Retrieve a [ScaffoldState] from the current
-/// [BuildContext] using [Scaffold.of].
+/// Can display [SnackBar]s and [BottomSheet]s. Retrieve a [ScaffoldState] from
+/// the current [BuildContext] using [Scaffold.of].
 class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
 
   // DRAWER API
@@ -2068,10 +1668,12 @@
 
   // SNACKBAR API
 
-  ScaffoldMessengerState _scaffoldMessenger;
+  final Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>> _snackBars = Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>>();
+  AnimationController _snackBarController;
+  Timer _snackBarTimer;
+  bool _accessibleNavigation;
 
-  /// [ScaffoldMessengerState.showSnackBar] shows a [SnackBar] at the bottom of
-  /// the scaffold. This method should not be used.
+  /// Shows a [SnackBar] at the bottom of the scaffold.
   ///
   /// A scaffold can show at most one snack bar at a time. If this function is
   /// called while another snack bar is already visible, the given snack bar
@@ -2080,14 +1682,12 @@
   ///
   /// To control how long a [SnackBar] remains visible, use [SnackBar.duration].
   ///
-  /// To remove the [SnackBar] with an exit animation, use
-  /// [ScaffoldMessengerState.hideCurrentSnackBar] or call
-  /// [ScaffoldFeatureController.close] on the returned [ScaffoldFeatureController].
-  /// To remove a [SnackBar] suddenly (without an animation), use
-  /// [ScaffoldMessengerState.removeCurrentSnackBar].
+  /// To remove the [SnackBar] with an exit animation, use [hideCurrentSnackBar]
+  /// or call [ScaffoldFeatureController.close] on the returned
+  /// [ScaffoldFeatureController]. To remove a [SnackBar] suddenly (without an
+  /// animation), use [removeCurrentSnackBar].
   ///
-  /// See [ScaffoldMessenger.of] for information about how to obtain the
-  /// [ScaffoldMessengerState].
+  /// See [Scaffold.of] for information about how to obtain the [ScaffoldState].
   ///
   /// {@tool dartpad --template=stateless_widget_scaffold_center}
   ///
@@ -2097,66 +1697,104 @@
   ///   Widget build(BuildContext context) {
   ///     return OutlinedButton(
   ///       onPressed: () {
-  ///         ScaffoldMessenger.of(context).showSnackBar(
+  ///         Scaffold.of(context).showSnackBar(
   ///           SnackBar(
-  ///             content: const Text('A SnackBar has been shown.'),
+  ///             content: Text('A SnackBar has been shown.'),
   ///           ),
   ///         );
   ///       },
-  ///       child: const Text('Show SnackBar'),
+  ///       child: Text('Show SnackBar'),
   ///     );
   ///   }
   /// ```
   /// {@end-tool}
-  ///
-  /// See also:
-  ///
-  ///   * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
-  // TODO(Piinks): Deprecate after customers are migrated
   ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(SnackBar snackbar) {
-    assert(debugCheckHasScaffoldMessenger(context));
-    return _scaffoldMessenger.showSnackBar(snackbar);
+    _snackBarController ??= SnackBar.createAnimationController(vsync: this)
+      ..addStatusListener(_handleSnackBarStatusChange);
+    if (_snackBars.isEmpty) {
+      assert(_snackBarController.isDismissed);
+      _snackBarController.forward();
+    }
+    ScaffoldFeatureController<SnackBar, SnackBarClosedReason> controller;
+    controller = ScaffoldFeatureController<SnackBar, SnackBarClosedReason>._(
+      // We provide a fallback key so that if back-to-back snackbars happen to
+      // match in structure, material ink splashes and highlights don't survive
+      // from one to the next.
+      snackbar.withAnimation(_snackBarController, fallbackKey: UniqueKey()),
+      Completer<SnackBarClosedReason>(),
+      () {
+        assert(_snackBars.first == controller);
+        hideCurrentSnackBar(reason: SnackBarClosedReason.hide);
+      },
+      null, // SnackBar doesn't use a builder function so setState() wouldn't rebuild it
+    );
+    setState(() {
+      _snackBars.addLast(controller);
+    });
+    return controller;
   }
 
-  /// [ScaffoldMessengerState.removeCurrentSnackBar] removes the current
-  /// [SnackBar] (if any) immediately. This method should not be used.
+  void _handleSnackBarStatusChange(AnimationStatus status) {
+    switch (status) {
+      case AnimationStatus.dismissed:
+        assert(_snackBars.isNotEmpty);
+        setState(() {
+          _snackBars.removeFirst();
+        });
+        if (_snackBars.isNotEmpty)
+          _snackBarController.forward();
+        break;
+      case AnimationStatus.completed:
+        setState(() {
+          assert(_snackBarTimer == null);
+          // build will create a new timer if necessary to dismiss the snack bar
+        });
+        break;
+      case AnimationStatus.forward:
+      case AnimationStatus.reverse:
+        break;
+    }
+  }
+
+  /// Removes the current [SnackBar] (if any) immediately.
   ///
   /// The removed snack bar does not run its normal exit animation. If there are
   /// any queued snack bars, they begin their entrance animation immediately.
-  ///
-  /// See also:
-  ///
-  ///   * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
-  // TODO(Piinks): Deprecate after customers are migrated
   void removeCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.remove }) {
-    assert(debugCheckHasScaffoldMessenger(context));
-    _scaffoldMessenger.removeCurrentSnackBar(reason: reason);
+    assert(reason != null);
+    if (_snackBars.isEmpty)
+      return;
+    final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
+    if (!completer.isCompleted)
+      completer.complete(reason);
+    _snackBarTimer?.cancel();
+    _snackBarTimer = null;
+    _snackBarController.value = 0.0;
   }
 
-  /// [ScaffoldMessengerState.hideCurrentSnackBar] removes the current
-  /// [SnackBar] by running its normal exit animation. This method should not be
-  /// used.
+  /// Removes the current [SnackBar] by running its normal exit animation.
   ///
   /// The closed completer is called after the animation is complete.
-  ///
-  /// See also:
-  ///
-  ///   * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
-  // TODO(Piinks): Deprecate after customers are migrated.
   void hideCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.hide }) {
-    assert(debugCheckHasScaffoldMessenger(context));
-    _scaffoldMessenger.hideCurrentSnackBar(reason: reason);
+    assert(reason != null);
+    if (_snackBars.isEmpty || _snackBarController.status == AnimationStatus.dismissed)
+      return;
+    final MediaQueryData mediaQuery = MediaQuery.of(context);
+    final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
+    if (mediaQuery.accessibleNavigation) {
+      _snackBarController.value = 0.0;
+      completer.complete(reason);
+    } else {
+      _snackBarController.reverse().then<void>((void value) {
+        assert(mounted);
+        if (!completer.isCompleted)
+          completer.complete(reason);
+      });
+    }
+    _snackBarTimer?.cancel();
+    _snackBarTimer = null;
   }
 
-  ScaffoldFeatureController<SnackBar, SnackBarClosedReason> _snackBar;
-
-  void _updateSnackBar() {
-    setState(() {
-        _snackBar = _scaffoldMessenger._snackBars.isNotEmpty
-          ? _scaffoldMessenger._snackBars.first
-          : null;
-    });
-  }
 
   // PERSISTENT BOTTOM SHEET API
 
@@ -2378,9 +2016,7 @@
   ///                     const Text('BottomSheet'),
   ///                     ElevatedButton(
   ///                       child: const Text('Close BottomSheet'),
-  ///                       onPressed: () {
-  ///                         Navigator.pop(context);
-  ///                       }
+  ///                       onPressed: () => Navigator.pop(context),
   ///                     )
   ///                   ],
   ///                 ),
@@ -2568,14 +2204,27 @@
 
   @override
   void didChangeDependencies() {
-    _scaffoldMessenger = ScaffoldMessenger.of(context);
-    _scaffoldMessenger?._register(this);
+    final MediaQueryData mediaQuery = MediaQuery.of(context);
+    // If we transition from accessible navigation to non-accessible navigation
+    // and there is a SnackBar that would have timed out that has already
+    // completed its timer, dismiss that SnackBar. If the timer hasn't finished
+    // yet, let it timeout as normal.
+    if (_accessibleNavigation == true
+      && !mediaQuery.accessibleNavigation
+      && _snackBarTimer != null
+      && !_snackBarTimer.isActive) {
+      hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
+    }
+    _accessibleNavigation = mediaQuery.accessibleNavigation;
     _maybeBuildPersistentBottomSheet();
     super.didChangeDependencies();
   }
 
   @override
   void dispose() {
+    _snackBarController?.dispose();
+    _snackBarTimer?.cancel();
+    _snackBarTimer = null;
     _geometryNotifier.dispose();
     for (final _StandardBottomSheet bottomSheet in _dismissedBottomSheets) {
       bottomSheet.animationController?.dispose();
@@ -2585,7 +2234,6 @@
     }
     _floatingActionButtonMoveController.dispose();
     _floatingActionButtonVisibilityController.dispose();
-    _scaffoldMessenger?._unregister(this);
     super.dispose();
   }
 
@@ -2699,6 +2347,28 @@
     final MediaQueryData mediaQuery = MediaQuery.of(context);
     final ThemeData themeData = Theme.of(context);
     final TextDirection textDirection = Directionality.of(context);
+    _accessibleNavigation = mediaQuery.accessibleNavigation;
+
+    if (_snackBars.isNotEmpty) {
+      final ModalRoute<dynamic> route = ModalRoute.of(context);
+      if (route == null || route.isCurrent) {
+        if (_snackBarController.isCompleted && _snackBarTimer == null) {
+          final SnackBar snackBar = _snackBars.first._widget;
+          _snackBarTimer = Timer(snackBar.duration, () {
+            assert(_snackBarController.status == AnimationStatus.forward ||
+                   _snackBarController.status == AnimationStatus.completed);
+            // Look up MediaQuery again in case the setting changed.
+            final MediaQueryData mediaQuery = MediaQuery.of(context);
+            if (mediaQuery.accessibleNavigation && snackBar.action != null)
+              return;
+            hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
+          });
+        }
+      } else {
+        _snackBarTimer?.cancel();
+        _snackBarTimer = null;
+      }
+    }
 
     final List<LayoutId> children = <LayoutId>[];
     _addIfNonNull(
@@ -2753,16 +2423,16 @@
 
     bool isSnackBarFloating = false;
     double snackBarWidth;
-    if (_snackBar != null) {
-      final SnackBarBehavior snackBarBehavior = _snackBar._widget.behavior
+    if (_snackBars.isNotEmpty) {
+      final SnackBarBehavior snackBarBehavior = _snackBars.first._widget.behavior
         ?? themeData.snackBarTheme.behavior
         ?? SnackBarBehavior.fixed;
       isSnackBarFloating = snackBarBehavior == SnackBarBehavior.floating;
-      snackBarWidth = _snackBar._widget.width;
+      snackBarWidth = _snackBars.first._widget.width;
 
       _addIfNonNull(
         children,
-        _snackBar._widget,
+        _snackBars.first._widget,
         _ScaffoldSlot.snackBar,
         removeLeftPadding: false,
         removeTopPadding: true,
@@ -2926,8 +2596,7 @@
 
 /// An interface for controlling a feature of a [Scaffold].
 ///
-/// Commonly obtained from [ScaffoldMessengerState.showSnackBar] or
-/// [ScaffoldState.showBottomSheet].
+/// Commonly obtained from [ScaffoldState.showSnackBar] or [ScaffoldState.showBottomSheet].
 class ScaffoldFeatureController<T extends Widget, U> {
   const ScaffoldFeatureController._(this._widget, this._completer, this.close, this.setState);
   final T _widget;
diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart
index be39856..39c1e7a 100644
--- a/packages/flutter/lib/src/material/snack_bar.dart
+++ b/packages/flutter/lib/src/material/snack_bar.dart
@@ -32,7 +32,7 @@
 
 /// Specify how a [SnackBar] was closed.
 ///
-/// The [ScaffoldMessengerState.showSnackBar] function returns a
+/// The [ScaffoldState.showSnackBar] function returns a
 /// [ScaffoldFeatureController]. The value of the controller's closed property
 /// is a Future that resolves to a SnackBarClosedReason. Applications that need
 /// to know how a snackbar was closed can use this value.
@@ -40,7 +40,7 @@
 /// Example:
 ///
 /// ```dart
-/// ScaffoldMessenger.of(context).showSnackBar(
+/// Scaffold.of(context).showSnackBar(
 ///   SnackBar( ... )
 /// ).closed.then((SnackBarClosedReason reason) {
 ///    ...
@@ -57,10 +57,10 @@
   swipe,
 
   /// The snack bar was closed by the [ScaffoldFeatureController] close callback
-  /// or by calling [ScaffoldMessengerState.hideCurrentSnackBar] directly.
+  /// or by calling [ScaffoldState.hideCurrentSnackBar] directly.
   hide,
 
-  /// The snack bar was closed by an call to [ScaffoldMessengerState.removeCurrentSnackBar].
+  /// The snack bar was closed by an call to [ScaffoldState.removeCurrentSnackBar].
   remove,
 
   /// The snack bar was closed because its timer expired.
@@ -123,7 +123,7 @@
       _haveTriggeredAction = true;
     });
     widget.onPressed();
-    ScaffoldMessenger.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.action);
+    Scaffold.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.action);
   }
 
   @override
@@ -146,8 +146,8 @@
 ///
 /// {@youtube 560 315 https://www.youtube.com/watch?v=zpO6n_oZWw0}
 ///
-/// To display a snack bar, call `ScaffoldMessenger.of(context).showSnackBar()`,
-/// passing an instance of [SnackBar] that describes the message.
+/// To display a snack bar, call `Scaffold.of(context).showSnackBar()`, passing
+/// an instance of [SnackBar] that describes the message.
 ///
 /// To control how long the [SnackBar] remains visible, specify a [duration].
 ///
@@ -156,11 +156,11 @@
 ///
 /// See also:
 ///
-///  * [ScaffoldMessenger.of], to obtain the current [ScaffoldMessengerState],
-///    which manages the display and animation of snack bars.
-///  * [ScaffoldMessengerState.showSnackBar], which displays a [SnackBar].
-///  * [ScaffoldMessengerState.removeCurrentSnackBar], which abruptly hides the
-///    currently displayed snack bar, if any, and allows the next to be displayed.
+///  * [Scaffold.of], to obtain the current [ScaffoldState], which manages the
+///    display and animation of snack bars.
+///  * [ScaffoldState.showSnackBar], which displays a [SnackBar].
+///  * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the currently
+///    displayed snack bar, if any, and allows the next to be displayed.
 ///  * [SnackBarAction], which is used to specify an [action] button to show
 ///    on the snack bar.
 ///  * [SnackBarThemeData], to configure the default property values for
@@ -289,7 +289,7 @@
   ///
   /// See also:
   ///
-  ///  * [ScaffoldMessengerState.removeCurrentSnackBar], which abruptly hides the
+  ///  * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the
   ///    currently displayed snack bar, if any, and allows the next to be
   ///    displayed.
   ///  * <https://material.io/design/components/snackbars.html>
@@ -301,7 +301,7 @@
   /// Called the first time that the snackbar is visible within a [Scaffold].
   final VoidCallback onVisible;
 
-  // API for ScaffoldMessengerState.showSnackBar():
+  // API for Scaffold.showSnackBar():
 
   /// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
   static AnimationController createAnimationController({ @required TickerProvider vsync }) {
@@ -516,14 +516,14 @@
       container: true,
       liveRegion: true,
       onDismiss: () {
-        ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
+        Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
       },
       child: Dismissible(
         key: const Key('dismissible'),
         direction: DismissDirection.down,
         resizeDuration: null,
         onDismissed: (DismissDirection direction) {
-          ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
+          Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
         },
         child: snackBar,
       ),
@@ -550,9 +550,7 @@
         child: snackBar,
       );
     }
-    return Hero(
-      child: ClipRect(child: snackBarTransition),
-      tag: '<SnackBar Hero tag - ${widget.content}>',
-    );
+
+    return ClipRect(child: snackBarTransition);
   }
 }
diff --git a/packages/flutter/lib/src/widgets/animated_list.dart b/packages/flutter/lib/src/widgets/animated_list.dart
index a01be9d..f463dd1 100644
--- a/packages/flutter/lib/src/widgets/animated_list.dart
+++ b/packages/flutter/lib/src/widgets/animated_list.dart
@@ -512,7 +512,6 @@
 /// class _SliverAnimatedListSampleState extends State<SliverAnimatedListSample> {
 ///   final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>();
 ///   final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
-///   final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
 ///   ListModel<int> _list;
 ///   int _selectedItem;
 ///   int _nextItem; // The next item inserted when the user presses the '+' button.
@@ -570,7 +569,7 @@
 ///         _selectedItem = null;
 ///       });
 ///     } else {
-///       _scaffoldMessengerKey.currentState.showSnackBar(SnackBar(
+///       _scaffoldKey.currentState.showSnackBar(SnackBar(
 ///         content: Text(
 ///           'Select an item to remove from the list.',
 ///           style: TextStyle(fontSize: 20),
@@ -582,7 +581,6 @@
 ///   @override
 ///   Widget build(BuildContext context) {
 ///     return MaterialApp(
-///       scaffoldMessengerKey: _scaffoldMessengerKey,
 ///       home: Scaffold(
 ///         key: _scaffoldKey,
 ///         body: CustomScrollView(
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index 82f2d10..b73c517 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -2084,52 +2084,33 @@
 /// widget can be used: the build context passed to the [Builder.builder]
 /// callback will be that of the [Builder] itself.
 ///
-/// For example, in the following snippet, the [ScaffoldState.showBottomSheet]
+/// For example, in the following snippet, the [ScaffoldState.showSnackBar]
 /// method is called on the [Scaffold] widget that the build method itself
 /// creates. If a [Builder] had not been used, and instead the `context`
 /// argument of the build method itself had been used, no [Scaffold] would have
 /// been found, and the [Scaffold.of] function would have returned null.
 ///
 /// ```dart
-/// @override
-/// Widget build(BuildContext context) {
-///   // here, Scaffold.of(context) returns null
-///   return Scaffold(
-///     appBar: AppBar(title: Text('Demo')),
-///     body: Builder(
-///       builder: (BuildContext context) {
-///         return TextButton(
-///           child: Text('BUTTON'),
-///           onPressed: () {
-///             Scaffold.of(context).showBottomSheet<void>(
-///               (BuildContext context) {
-///                 return Container(
-///                   alignment: Alignment.center,
-///                   height: 200,
-///                   color: Colors.amber,
-///                   child: Center(
-///                     child: Column(
-///                       mainAxisSize: MainAxisSize.min,
-///                       children: <Widget>[
-///                         const Text('BottomSheet'),
-///                         ElevatedButton(
-///                           child: const Text('Close BottomSheet'),
-///                           onPressed: () {
-///                             Navigator.pop(context),
-///                           },
-///                         )
-///                       ],
-///                     ),
-///                   ),
-///                 );
-///               },
-///             );
-///           },
-///         );
-///       },
-///     )
-///   );
-/// }
+///   @override
+///   Widget build(BuildContext context) {
+///     // here, Scaffold.of(context) returns null
+///     return Scaffold(
+///       appBar: AppBar(title: Text('Demo')),
+///       body: Builder(
+///         builder: (BuildContext context) {
+///           return TextButton(
+///             child: Text('BUTTON'),
+///             onPressed: () {
+///               // here, Scaffold.of(context) returns the locally created Scaffold
+///               Scaffold.of(context).showSnackBar(SnackBar(
+///                 content: Text('Hello.')
+///               ));
+///             }
+///           );
+///         }
+///       )
+///     );
+///   }
 /// ```
 ///
 /// The [BuildContext] for a particular widget can change location over time as
diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart
index 9ff158c..5461742 100644
--- a/packages/flutter/test/material/debug_test.dart
+++ b/packages/flutter/test/material/debug_test.dart
@@ -164,8 +164,6 @@
       '     _InheritedTheme\n'
       '     Theme\n'
       '     AnimatedTheme\n'
-      '     _ScaffoldMessengerScope\n'
-      '     ScaffoldMessenger\n'
       '     Builder\n'
       '     DefaultTextStyle\n'
       '     CustomPaint\n'
@@ -198,50 +196,4 @@
       '   or WidgetsApp widget at the top of your application widget tree.\n',
     ));
   });
-
-  testWidgets('debugCheckHasScaffoldMessenger control test', (WidgetTester tester) async {
-    final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
-    await tester.pumpWidget(Directionality(
-      textDirection: TextDirection.ltr,
-      child: MediaQuery(
-        data: const MediaQueryData(),
-        child: Scaffold(
-          key: _scaffoldKey,
-          body: Container(),
-        ),
-      ),
-    ));
-    FlutterError error;
-    try {
-      _scaffoldKey.currentState.showSnackBar(const SnackBar(content: Text('Something is missing here')));
-    } on FlutterError catch (e) {
-      error = e;
-    } finally {
-      expect(error.diagnostics.length, 5);
-      expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>());
-      expect(error.diagnostics[3], isA<DiagnosticsBlock>());
-      expect(error.diagnostics[4].level, DiagnosticLevel.hint);
-      expect(
-        error.diagnostics[4].toStringDeep(),
-        equalsIgnoringHashCodes(
-          'Typically, the ScaffoldMessenger widget is introduced by the\n'
-          'MaterialApp at the top of your application widget tree.\n',
-        ),
-      );
-      expect(error.toStringDeep(), equalsIgnoringHashCodes(
-        'FlutterError\n'
-          '   No ScaffoldMessenger widget found.\n'
-          '   Scaffold widgets require a ScaffoldMessenger widget ancestor.\n'
-          '   The specific widget that could not find a ScaffoldMessenger\n'
-          '   ancestor was:\n'
-          '     Scaffold-[LabeledGlobalKey<ScaffoldState>#d60fa]\n'
-          '   The ancestors of this widget were:\n'
-          '     MediaQuery\n'
-          '     Directionality\n'
-          '     [root]\n'
-          '   Typically, the ScaffoldMessenger widget is introduced by the\n'
-          '   MaterialApp at the top of your application widget tree.\n'
-      ));
-    }
-  });
 }
diff --git a/packages/flutter/test/material/floating_action_button_location_test.dart b/packages/flutter/test/material/floating_action_button_location_test.dart
index 3086003..c919cf7 100644
--- a/packages/flutter/test/material/floating_action_button_location_test.dart
+++ b/packages/flutter/test/material/floating_action_button_location_test.dart
@@ -649,7 +649,7 @@
               builder: (BuildContext context) {
                 return FloatingActionButton(
                   onPressed: () {
-                    ScaffoldMessenger.of(context).showSnackBar(
+                    Scaffold.of(context).showSnackBar(
                       const SnackBar(content: Text('Snacky!')),
                     );
                   },
diff --git a/packages/flutter/test/material/snack_bar_test.dart b/packages/flutter/test/material/snack_bar_test.dart
index 481854f..6e28582 100644
--- a/packages/flutter/test/material/snack_bar_test.dart
+++ b/packages/flutter/test/material/snack_bar_test.dart
@@ -18,7 +18,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
+                Scaffold.of(context).showSnackBar(const SnackBar(
                   content: Text(helloSnackBar),
                   duration: Duration(seconds: 2),
                 ));
@@ -64,7 +64,7 @@
             return GestureDetector(
               onTap: () {
                 snackBarCount += 1;
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('bar$snackBarCount'),
                   duration: const Duration(seconds: 2),
                 ));
@@ -141,7 +141,7 @@
             return GestureDetector(
               onTap: () {
                 snackBarCount += 1;
-                lastController = ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                lastController = Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('bar$snackBarCount'),
                   duration: Duration(seconds: time),
                 ));
@@ -225,7 +225,7 @@
             return GestureDetector(
               onTap: () {
                 snackBarCount += 1;
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text('bar$snackBarCount'),
                   duration: const Duration(seconds: 2),
                 ));
@@ -268,7 +268,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('I am a snack bar.'),
                   duration: const Duration(seconds: 2),
                   action: SnackBarAction(
@@ -309,7 +309,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(
+                    Scaffold.of(context).showSnackBar(
                       SnackBar(
                         content: const Text('I am a snack bar.'),
                         duration: const Duration(seconds: 2),
@@ -351,7 +351,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(
+                  Scaffold.of(context).showSnackBar(
                     SnackBar(
                       content: const Text('I am a snack bar.'),
                       duration: const Duration(seconds: 2),
@@ -389,7 +389,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(
+                    Scaffold.of(context).showSnackBar(
                       SnackBar(
                         content: const Text('I am a snack bar.'),
                         margin: const EdgeInsets.all(padding),
@@ -476,7 +476,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(
+                    Scaffold.of(context).showSnackBar(
                       const SnackBar(
                         content: Text('I am a snack bar.'),
                         padding: EdgeInsets.all(padding),
@@ -520,7 +520,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(
+                    Scaffold.of(context).showSnackBar(
                       SnackBar(
                         content: const Text('I am a snack bar.'),
                         width: width,
@@ -558,7 +558,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(
+                  Scaffold.of(context).showSnackBar(
                     SnackBar(
                       content: const Text('I am a snack bar.'),
                       duration: const Duration(seconds: 2),
@@ -611,7 +611,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                  Scaffold.of(context).showSnackBar(SnackBar(
                     content: const Text('I am a snack bar.'),
                     duration: const Duration(seconds: 2),
                     action: SnackBarAction(label: 'ACTION', onPressed: () { }),
@@ -666,7 +666,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                    Scaffold.of(context).showSnackBar(SnackBar(
                       content: const Text('I am a snack bar.'),
                       duration: const Duration(seconds: 2),
                       action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -717,7 +717,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                  Scaffold.of(context).showSnackBar(SnackBar(
                     content: const Text('I am a snack bar.'),
                     duration: const Duration(seconds: 2),
                     action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -771,7 +771,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                  Scaffold.of(context).showSnackBar(SnackBar(
                     content: const Text('I am a snack bar.'),
                     duration: const Duration(seconds: 2),
                     action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -829,7 +829,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                    Scaffold.of(context).showSnackBar(SnackBar(
                       content: const Text('I am a snack bar.'),
                       duration: const Duration(seconds: 2),
                       action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -861,18 +861,18 @@
     });
 
   testWidgets('SnackBarClosedReason', (WidgetTester tester) async {
-    final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
+    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
     bool actionPressed = false;
     SnackBarClosedReason closedReason;
 
     await tester.pumpWidget(MaterialApp(
-      scaffoldMessengerKey: scaffoldMessengerKey,
       home: Scaffold(
+        key: scaffoldKey,
         body: Builder(
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('snack'),
                   duration: const Duration(seconds: 2),
                   action: SnackBarAction(
@@ -917,14 +917,14 @@
     // Pop up the snack bar and then remove it.
     await tester.tap(find.text('X'));
     await tester.pump(const Duration(milliseconds: 750));
-    scaffoldMessengerKey.currentState.removeCurrentSnackBar();
+    scaffoldKey.currentState.removeCurrentSnackBar();
     await tester.pumpAndSettle(const Duration(seconds: 1));
     expect(closedReason, equals(SnackBarClosedReason.remove));
 
     // Pop up the snack bar and then hide it.
     await tester.tap(find.text('X'));
     await tester.pump(const Duration(milliseconds: 750));
-    scaffoldMessengerKey.currentState.hideCurrentSnackBar();
+    scaffoldKey.currentState.hideCurrentSnackBar();
     await tester.pumpAndSettle(const Duration(seconds: 1));
     expect(closedReason, equals(SnackBarClosedReason.hide));
 
@@ -944,27 +944,25 @@
     await tester.pumpWidget(MaterialApp(
       home: MediaQuery(
         data: const MediaQueryData(accessibleNavigation: true),
-        child: ScaffoldMessenger(
-          child: Builder(
+        child: Scaffold(
+          key: scaffoldKey,
+          body: Builder(
             builder: (BuildContext context) {
-              return Scaffold(
-                key: scaffoldKey,
-                body: GestureDetector(
-                  onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                      content: const Text('snack'),
-                      duration: const Duration(seconds: 1),
-                      action: SnackBarAction(
-                        label: 'ACTION',
-                        onPressed: () { },
-                      ),
-                    ));
-                  },
-                  child: const Text('X'),
-                ),
+              return GestureDetector(
+                onTap: () {
+                  Scaffold.of(context).showSnackBar(SnackBar(
+                    content: const Text('snack'),
+                    duration: const Duration(seconds: 1),
+                    action: SnackBarAction(
+                      label: 'ACTION',
+                      onPressed: () { },
+                    ),
+                  ));
+                },
+                child: const Text('X'),
               );
-            }
-          )
+            },
+          ),
         ),
       ),
     ));
@@ -986,29 +984,29 @@
     final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
 
     await tester.pumpWidget(MaterialApp(
-      home: MediaQuery(
-        data: const MediaQueryData(accessibleNavigation: true),
-        child: ScaffoldMessenger(
-          child: Builder(builder: (BuildContext context) {
-            return Scaffold(
-              key: scaffoldKey,
-              body: GestureDetector(
-                onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                    content: const Text('snack'),
-                    duration: const Duration(seconds: 1),
-                    action: SnackBarAction(
-                      label: 'ACTION',
-                      onPressed: () { },
-                    ),
-                  ));
-                },
-                child: const Text('X'),
-              ),
-            );
-          }),
+        home: MediaQuery(
+            data: const MediaQueryData(accessibleNavigation: true),
+            child: Scaffold(
+                key: scaffoldKey,
+                body: Builder(
+                  builder: (BuildContext context) {
+                    return GestureDetector(
+                        onTap: () {
+                          Scaffold.of(context).showSnackBar(SnackBar(
+                            content: const Text('snack'),
+                            duration: const Duration(seconds: 1),
+                            action: SnackBarAction(
+                                label: 'ACTION',
+                                onPressed: () { },
+                            ),
+                          ));
+                        },
+                        child: const Text('X'),
+                    );
+                  },
+                ),
+            ),
         ),
-      )
     ));
     await tester.tap(find.text('X'));
     await tester.pumpAndSettle();
@@ -1033,7 +1031,7 @@
                 builder: (BuildContext context) {
                   return GestureDetector(
                       onTap: () {
-                        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
+                        Scaffold.of(context).showSnackBar(const SnackBar(
                             content: Text(helloSnackBar),
                         ));
                       },
@@ -1082,7 +1080,7 @@
                       builder: (BuildContext context) {
                         return GestureDetector(
                             onTap: () {
-                              ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                              Scaffold.of(context).showSnackBar(SnackBar(
                                   content: const Text('test'),
                                   action: SnackBarAction(label: 'foo', onPressed: () { }),
                               ));
@@ -1128,7 +1126,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: Text(nonconst('hello')),
                   duration: null,
                 ));
@@ -1158,7 +1156,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('hello'),
                   duration: const Duration(seconds: 1),
                   onVisible: () {
@@ -1195,14 +1193,14 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('hello'),
                   duration: const Duration(seconds: 1),
                   onVisible: () {
                     called += 1;
                   },
                 ));
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('hello 2'),
                   duration: const Duration(seconds: 1),
                   onVisible: () {
@@ -1249,8 +1247,8 @@
             ),
           );
 
-          final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
-          scaffoldMessengerState.showSnackBar(snackBar);
+          final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
+          scaffoldState.showSnackBar(snackBar);
 
           await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
 
@@ -1280,8 +1278,8 @@
             ),
           );
 
-          final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
-          scaffoldMessengerState.showSnackBar(snackBar);
+          final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
+          scaffoldState.showSnackBar(snackBar);
 
           await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
 
@@ -1300,8 +1298,9 @@
       testWidgets(
         'Padding of $behavior is not consumed by viewInsets',
         (WidgetTester tester) async {
-          final Widget child = MaterialApp(
-            home: Scaffold(
+          final Widget child = Directionality(
+            textDirection: TextDirection.ltr,
+            child: Scaffold(
               resizeToAvoidBottomInset: false,
               floatingActionButton: FloatingActionButton(
                 child: const Icon(Icons.send),
@@ -1311,7 +1310,7 @@
                 builder: (BuildContext context) {
                   return GestureDetector(
                     onTap: () {
-                      ScaffoldMessenger.of(context).showSnackBar(
+                      Scaffold.of(context).showSnackBar(
                         SnackBar(
                           content: const Text('I am a snack bar.'),
                           duration: const Duration(seconds: 2),
@@ -1375,8 +1374,8 @@
           ),
         );
 
-        final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
-        scaffoldMessengerState.showSnackBar(
+        final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
+        scaffoldState.showSnackBar(
           const SnackBar(
             content: Text('Snackbar text'),
             behavior: SnackBarBehavior.fixed,
@@ -1411,7 +1410,7 @@
               builder: (BuildContext context) {
                 return GestureDetector(
                   onTap: () {
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                    Scaffold.of(context).showSnackBar(SnackBar(
                       content: const Text('I am a snack bar.'),
                       duration: const Duration(seconds: 2),
                       action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -1453,8 +1452,8 @@
           ),
         );
 
-        final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
-        scaffoldMessengerState.showSnackBar(
+        final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
+        scaffoldState.showSnackBar(
           const SnackBar(
             content: Text('SnackBar text'),
             behavior: SnackBarBehavior.fixed,
@@ -1490,8 +1489,8 @@
           ),
         );
 
-        final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
-        scaffoldMessengerState.showSnackBar(
+        final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
+        scaffoldState.showSnackBar(
           const SnackBar(
             content: Text('SnackBar text'),
             behavior: SnackBarBehavior.floating,
@@ -1507,74 +1506,4 @@
       },
     );
   });
-
-  testWidgets('SnackBars hero across transitions', (WidgetTester tester) async {
-    const String snackBarText = 'hello snackbar';
-    const String firstHeader = 'home';
-    const String secondHeader = 'second';
-    const Key snackTarget = Key('snack-target');
-    const Key transitionTarget = Key('transition-target');
-
-    Widget _buildApp() {
-      return MaterialApp(
-        routes: <String, WidgetBuilder> {
-          '/': (BuildContext context) {
-            return Scaffold(
-              appBar: AppBar(title: const Text(firstHeader)),
-              body: Center(
-                child: ElevatedButton(
-                  key: transitionTarget,
-                  child: const Text('PUSH'),
-                  onPressed: () {
-                    Navigator.of(context).pushNamed('/second');
-                  },
-                ),
-              ),
-              floatingActionButton: FloatingActionButton(
-                key: snackTarget,
-                onPressed: () async {
-                  ScaffoldMessenger.of(context).showSnackBar(
-                    const SnackBar(
-                      content: Text(snackBarText),
-                    ),
-                  );
-                },
-                child: const Text('X'),
-              ),
-            );
-          },
-          '/second': (BuildContext context) => Scaffold(appBar: AppBar(title: const Text(secondHeader)),
-      ),
-        }
-      );
-    }
-    await tester.pumpWidget(_buildApp());
-
-    expect(find.text(snackBarText), findsNothing);
-    expect(find.text(firstHeader), findsOneWidget);
-    expect(find.text(secondHeader), findsNothing);
-
-    // Present SnackBar
-    await tester.tap(find.byKey(snackTarget));
-    await tester.pump(); // schedule animation
-    expect(find.text(snackBarText), findsOneWidget);
-    await tester.pump(); // begin animation
-    expect(find.text(snackBarText), findsOneWidget);
-    await tester.pump(const Duration(milliseconds: 750));
-    expect(find.text(snackBarText), findsOneWidget);
-    // Push new route
-    await tester.tap(find.byKey(transitionTarget));
-    await tester.pump();
-    expect(find.text(snackBarText), findsOneWidget);
-    expect(find.text(firstHeader), findsOneWidget);
-    expect(find.text(secondHeader, skipOffstage: false), findsOneWidget);
-    await tester.pump();
-    expect(find.text(snackBarText), findsOneWidget);
-    expect(find.text(firstHeader), findsOneWidget);
-    expect(find.text(secondHeader), findsOneWidget);
-    await tester.pump(const Duration(milliseconds: 750));
-    expect(find.text(snackBarText), findsOneWidget);
-    expect(find.text(firstHeader), findsNothing);
-    expect(find.text(secondHeader), findsOneWidget);
-  });
 }
diff --git a/packages/flutter/test/material/snack_bar_theme_test.dart b/packages/flutter/test/material/snack_bar_theme_test.dart
index fbbbec0..a49f5bc 100644
--- a/packages/flutter/test/material/snack_bar_theme_test.dart
+++ b/packages/flutter/test/material/snack_bar_theme_test.dart
@@ -73,7 +73,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text(text),
                   duration: const Duration(seconds: 2),
                   action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -110,7 +110,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                  Scaffold.of(context).showSnackBar(SnackBar(
                     content: const Text(text),
                     duration: const Duration(seconds: 2),
                     action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -153,7 +153,7 @@
             builder: (BuildContext context) {
               return GestureDetector(
                 onTap: () {
-                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                  Scaffold.of(context).showSnackBar(SnackBar(
                     backgroundColor: backgroundColor,
                     elevation: elevation,
                     shape: shape,
@@ -200,7 +200,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('I am a snack bar.'),
                   duration: const Duration(seconds: 2),
                   action: SnackBarAction(label: 'ACTION', onPressed: () {}),
@@ -242,7 +242,7 @@
           builder: (BuildContext context) {
             return GestureDetector(
               onTap: () {
-                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                Scaffold.of(context).showSnackBar(SnackBar(
                   content: const Text('I am a snack bar.'),
                   duration: const Duration(seconds: 2),
                   action: SnackBarAction(label: 'ACTION', onPressed: () {}),