[flutter roll] replicate the revert in release branch  (#101947)

* accept all incoming

* Update debug_test.dart

* Update debug_test.dart

Co-authored-by: Kate Lovett <katelovett@google.com>
diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart
index 65bd565..086095c 100644
--- a/packages/flutter/lib/src/material/app_bar.dart
+++ b/packages/flutter/lib/src/material/app_bar.dart
@@ -737,24 +737,24 @@
   static const double _defaultElevation = 4.0;
   static const Color _defaultShadowColor = Color(0xFF000000);
 
-  ScrollMetricsNotificationObserverState? _scrollMetricsNotificationObserver;
+  ScrollNotificationObserverState? _scrollNotificationObserver;
   bool _scrolledUnder = false;
 
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
-    if (_scrollMetricsNotificationObserver != null)
-      _scrollMetricsNotificationObserver!.removeListener(_handleScrollMetricsNotification);
-    _scrollMetricsNotificationObserver = ScrollMetricsNotificationObserver.of(context);
-    if (_scrollMetricsNotificationObserver != null)
-      _scrollMetricsNotificationObserver!.addListener(_handleScrollMetricsNotification);
+    if (_scrollNotificationObserver != null)
+      _scrollNotificationObserver!.removeListener(_handleScrollNotification);
+    _scrollNotificationObserver = ScrollNotificationObserver.of(context);
+    if (_scrollNotificationObserver != null)
+      _scrollNotificationObserver!.addListener(_handleScrollNotification);
   }
 
   @override
   void dispose() {
-    if (_scrollMetricsNotificationObserver != null) {
-      _scrollMetricsNotificationObserver!.removeListener(_handleScrollMetricsNotification);
-      _scrollMetricsNotificationObserver = null;
+    if (_scrollNotificationObserver != null) {
+      _scrollNotificationObserver!.removeListener(_handleScrollNotification);
+      _scrollNotificationObserver = null;
     }
     super.dispose();
   }
@@ -767,34 +767,18 @@
     Scaffold.of(context).openEndDrawer();
   }
 
-  void _handleScrollMetricsNotification(ScrollMetricsNotification notification) {
-    final bool oldScrolledUnder = _scrolledUnder;
-    final ScrollMetrics metrics = notification.metrics;
-
-    if (notification.depth != 0) {
-      _scrolledUnder = false;
-    } else {
-      switch (metrics.axisDirection) {
-        case AxisDirection.up:
-          // Scroll view is reversed
-          _scrolledUnder = metrics.extentAfter > 0;
-          break;
-        case AxisDirection.down:
-          _scrolledUnder = metrics.extentBefore > 0;
-          break;
-        case AxisDirection.right:
-        case AxisDirection.left:
-        // Scrolled under is only supported in the vertical axis.
-          _scrolledUnder = false;
-          break;
+  void _handleScrollNotification(ScrollNotification notification) {
+    if (notification is ScrollUpdateNotification) {
+      final bool oldScrolledUnder = _scrolledUnder;
+      _scrolledUnder = notification.depth == 0
+          && notification.metrics.extentBefore > 0
+          && notification.metrics.axis == Axis.vertical;
+      if (_scrolledUnder != oldScrolledUnder) {
+        setState(() {
+          // React to a change in MaterialState.scrolledUnder
+        });
       }
     }
-
-    if (_scrolledUnder != oldScrolledUnder) {
-      setState(() {
-        // React to a change in MaterialState.scrolledUnder
-      });
-    }
   }
 
   Color _resolveColor(Set<MaterialState> states, Color? widgetColor, Color? themeColor, Color defaultColor) {
diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart
index edd0b95..e5114c1 100644
--- a/packages/flutter/lib/src/material/scaffold.dart
+++ b/packages/flutter/lib/src/material/scaffold.dart
@@ -3096,7 +3096,7 @@
     return _ScaffoldScope(
       hasDrawer: hasDrawer,
       geometryNotifier: _geometryNotifier,
-      child: ScrollMetricsNotificationObserver(
+      child: ScrollNotificationObserver(
         child: Material(
           color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
           child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
diff --git a/packages/flutter/lib/src/widgets/scroll_notification_observer.dart b/packages/flutter/lib/src/widgets/scroll_notification_observer.dart
index bb51e1d..7829cbd 100644
--- a/packages/flutter/lib/src/widgets/scroll_notification_observer.dart
+++ b/packages/flutter/lib/src/widgets/scroll_notification_observer.dart
@@ -9,7 +9,6 @@
 import 'framework.dart';
 import 'notification_listener.dart';
 import 'scroll_notification.dart';
-import 'scroll_position.dart';
 
 /// A [ScrollNotification] listener for [ScrollNotificationObserver].
 ///
@@ -173,170 +172,3 @@
     super.dispose();
   }
 }
-
-/// A [ScrollMetricsNotification] listener for [ScrollMetricsNotificationObserver].
-///
-/// [ScrollMetricsNotificationObserver] is similar to
-/// [NotificationListener]. It supports a listener list instead of
-/// just a single listener and its listeners run unconditionally, they
-/// do not require a gating boolean return value.
-typedef ScrollMetricsNotificationCallback = void Function(ScrollMetricsNotification notification);
-
-class _ScrollMetricsNotificationObserverScope extends InheritedWidget {
-  const _ScrollMetricsNotificationObserverScope({
-    Key? key,
-    required Widget child,
-    required ScrollMetricsNotificationObserverState scrollMetricsNotificationObserverState,
-  }) : _scrollMetricsNotificationObserverState = scrollMetricsNotificationObserverState,
-        super(key: key, child: child);
-
-  final ScrollMetricsNotificationObserverState  _scrollMetricsNotificationObserverState;
-
-  @override
-  bool updateShouldNotify(_ScrollMetricsNotificationObserverScope old) {
-    return _scrollMetricsNotificationObserverState != old._scrollMetricsNotificationObserverState;
-  }
-}
-
-class _MetricsListenerEntry extends LinkedListEntry<_MetricsListenerEntry> {
-  _MetricsListenerEntry(this.listener);
-  final ScrollMetricsNotificationCallback listener;
-}
-
-/// Notifies its listeners when a descendant ScrollMetrics are
-/// initialized or updated.
-///
-/// To add a listener to a [ScrollMetricsNotificationObserver] ancestor:
-/// ```dart
-/// void listener(ScrollMetricsNotification notification) {
-///   // Do something, maybe setState()
-/// }
-/// ScrollMetricsNotificationObserver.of(context).addListener(listener)
-/// ```
-///
-/// To remove the listener from a [ScrollMetricsNotificationObserver] ancestor:
-/// ```dart
-/// ScrollMetricsNotificationObserver.of(context).removeListener(listener);
-/// ```
-///
-/// Stateful widgets that share an ancestor [ScrollMetricsNotificationObserver]
-/// typically add a listener in [State.didChangeDependencies] (removing the old
-/// one if necessary) and remove the listener in their [State.dispose] method.
-///
-/// This widget is similar to [NotificationListener]. It supports
-/// a listener list instead of just a single listener and its listeners
-/// run unconditionally, they do not require a gating boolean return value.
-class ScrollMetricsNotificationObserver extends StatefulWidget {
-  /// Create a [ScrollMetricsNotificationObserver].
-  ///
-  /// The [child] parameter must not be null.
-  const ScrollMetricsNotificationObserver({
-    Key? key,
-    required this.child,
-  }) : assert(child != null), super(key: key);
-
-  /// The subtree below this widget.
-  final Widget child;
-
-  /// The closest instance of this class that encloses the given context.
-  ///
-  /// If there is no enclosing [ScrollMetricsNotificationObserver] widget, then
-  /// null is returned.
-  static ScrollMetricsNotificationObserverState? of(BuildContext context) {
-    return context.dependOnInheritedWidgetOfExactType<_ScrollMetricsNotificationObserverScope>()?._scrollMetricsNotificationObserverState;
-  }
-
-  @override
-  ScrollMetricsNotificationObserverState createState() => ScrollMetricsNotificationObserverState();
-}
-
-/// The listener list state for a [ScrollMetricsNotificationObserver] returned
-/// by [ScrollMetricsNotificationObserver.of].
-///
-/// [ScrollMetricsNotificationObserver] is similar to
-/// [NotificationListener]. It supports a listener list instead of
-/// just a single listener and its listeners run unconditionally, they
-/// do not require a gating boolean return value.
-class ScrollMetricsNotificationObserverState extends State<ScrollMetricsNotificationObserver> {
-  LinkedList<_MetricsListenerEntry>? _listeners = LinkedList<_MetricsListenerEntry>();
-
-  bool _debugAssertNotDisposed() {
-    assert(() {
-      if (_listeners == null) {
-        throw FlutterError(
-          'A $runtimeType was used after being disposed.\n'
-              'Once you have called dispose() on a $runtimeType, it can no longer be used.',
-        );
-      }
-      return true;
-    }());
-    return true;
-  }
-
-  /// Add a [ScrollMetricsNotificationCallback] that will be called each time
-  /// a descendant scrolls.
-  void addListener(ScrollMetricsNotificationCallback listener) {
-    assert(_debugAssertNotDisposed());
-    _listeners!.add(_MetricsListenerEntry(listener));
-  }
-
-  /// Remove the specified [ScrollMetricsNotificationCallback].
-  void removeListener(ScrollMetricsNotificationCallback listener) {
-    assert(_debugAssertNotDisposed());
-    for (final _MetricsListenerEntry entry in _listeners!) {
-      if (entry.listener == listener) {
-        entry.unlink();
-        return;
-      }
-    }
-  }
-
-  void _notifyListeners(ScrollMetricsNotification notification) {
-    assert(_debugAssertNotDisposed());
-    if (_listeners!.isEmpty)
-      return;
-
-    final List<_MetricsListenerEntry> localListeners = List<_MetricsListenerEntry>.of(_listeners!);
-    for (final _MetricsListenerEntry entry in localListeners) {
-      try {
-        if (entry.list != null)
-          entry.listener(notification);
-      } catch (exception, stack) {
-        FlutterError.reportError(FlutterErrorDetails(
-          exception: exception,
-          stack: stack,
-          library: 'widget library',
-          context: ErrorDescription('while dispatching notifications for $runtimeType'),
-          informationCollector: () => <DiagnosticsNode>[
-            DiagnosticsProperty<ScrollMetricsNotificationObserverState>(
-              'The $runtimeType sending notification was',
-              this,
-              style: DiagnosticsTreeStyle.errorProperty,
-            ),
-          ],
-        ));
-      }
-    }
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return NotificationListener<ScrollMetricsNotification>(
-      onNotification: (ScrollMetricsNotification notification) {
-        _notifyListeners(notification);
-        return false;
-      },
-      child: _ScrollMetricsNotificationObserverScope(
-        scrollMetricsNotificationObserverState: this,
-        child: widget.child,
-      ),
-    );
-  }
-
-  @override
-  void dispose() {
-    assert(_debugAssertNotDisposed());
-    _listeners = null;
-    super.dispose();
-  }
-}
diff --git a/packages/flutter/test/material/app_bar_test.dart b/packages/flutter/test/material/app_bar_test.dart
index 2c95313..3f78736 100644
--- a/packages/flutter/test/material/app_bar_test.dart
+++ b/packages/flutter/test/material/app_bar_test.dart
@@ -2568,457 +2568,311 @@
     expect(actionIconTheme.color, foregroundColor);
   });
 
-  group('MaterialStateColor scrolledUnder', () {
+  testWidgets('SliverAppBar.backgroundColor MaterialStateColor scrolledUnder', (WidgetTester tester) async {
     const double collapsedHeight = kToolbarHeight;
     const double expandedHeight = 200.0;
     const Color scrolledColor = Color(0xff00ff00);
     const Color defaultColor = Color(0xff0000ff);
 
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: CustomScrollView(
+            slivers: <Widget>[
+              SliverAppBar(
+                elevation: 0,
+                backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
+                  return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
+                }),
+                expandedHeight: expandedHeight,
+                pinned: true,
+              ),
+              SliverList(
+                delegate: SliverChildListDelegate(
+                    <Widget>[
+                      Container(height: 1200.0, color: Colors.teal),
+                    ],
+                ),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+
     Finder findAppBarMaterial() {
-      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
+      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
     }
 
-    Color? getAppBarBackgroundColor(WidgetTester tester) {
+    Color? getAppBarBackgroundColor() {
       return tester.widget<Material>(findAppBarMaterial()).color;
     }
 
-    group('SliverAppBar', () {
-      Widget _buildSliverApp({
-        required double contentHeight,
-        bool reverse = false,
-        bool includeFlexibleSpace = false,
-      }) {
-        return MaterialApp(
-          home: Scaffold(
-            body: CustomScrollView(
-              reverse: reverse,
-              slivers: <Widget>[
-                SliverAppBar(
-                  elevation: 0,
-                  backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
-                    return states.contains(MaterialState.scrolledUnder)
-                      ? scrolledColor
-                      : defaultColor;
-                  }),
-                  expandedHeight: expandedHeight,
-                  pinned: true,
-                  flexibleSpace: includeFlexibleSpace
-                      ? const FlexibleSpaceBar(title: Text('SliverAppBar'))
-                      : null,
-                ),
-                SliverList(
-                  delegate: SliverChildListDelegate(
-                    <Widget>[
-                      Container(height: contentHeight, color: Colors.teal),
-                    ],
-                  ),
-                ),
-              ],
-            ),
-          ),
-        );
-      }
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
 
-      testWidgets('backgroundColor', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(contentHeight: 1200.0)
-        );
+    TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(0.0, -expandedHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
+    expect(getAppBarBackgroundColor(), scrolledColor);
+    expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
 
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, -expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
+    gesture = await tester.startGesture(const Offset(50.0, 300.0));
+    await gesture.moveBy(const Offset(0.0, expandedHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
 
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
+  });
 
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
+  testWidgets('SliverAppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
+    const double collapsedHeight = kToolbarHeight;
+    const double expandedHeight = 200.0;
+    const Color scrolledColor = Color(0xff00ff00);
+    const Color defaultColor = Color(0xff0000ff);
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-
-      testWidgets('backgroundColor with FlexibleSpace', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(contentHeight: 1200.0, includeFlexibleSpace: true)
-        );
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, -expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-
-      testWidgets('backgroundColor - reverse', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(contentHeight: 1200.0, reverse: true)
-        );
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, -expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-
-      testWidgets('backgroundColor with FlexibleSpace - reverse', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(
-            contentHeight: 1200.0,
-            reverse: true,
-            includeFlexibleSpace: true,
-          )
-        );
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, -expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-
-      testWidgets('backgroundColor - not triggered in reverse for short content', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(contentHeight: 200, reverse: true)
-        );
-
-        // In reverse, the content here is not long enough to scroll under the app
-        // bar.
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-
-        final TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-
-      testWidgets('backgroundColor with FlexibleSpace - not triggered in reverse for short content', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildSliverApp(
-            contentHeight: 200,
-            reverse: true,
-            includeFlexibleSpace: true,
-          )
-        );
-
-        // In reverse, the content here is not long enough to scroll under the app
-        // bar.
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-
-        final TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, expandedHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
-      });
-    });
-
-    group('AppBar', () {
-      Widget _buildAppBar({
-        required double contentHeight,
-        bool reverse = false,
-        bool includeFlexibleSpace = false
-      }) {
-        return MaterialApp(
-          home: Scaffold(
-            appBar: AppBar(
-              elevation: 0,
-              backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
-                return states.contains(MaterialState.scrolledUnder)
-                  ? scrolledColor
-                  : defaultColor;
-              }),
-              title: const Text('AppBar'),
-              flexibleSpace: includeFlexibleSpace
-                ? const FlexibleSpaceBar(title: Text('FlexibleSpace'))
-                : null,
-            ),
-            body: ListView(
-              reverse: reverse,
-              children: <Widget>[
-                Container(height: contentHeight, color: Colors.teal),
-              ],
-            ),
-          ),
-        );
-      }
-
-      testWidgets('backgroundColor', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(contentHeight: 1200.0)
-        );
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
-
-      testWidgets('backgroundColor with FlexibleSpace', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(contentHeight: 1200.0, includeFlexibleSpace: true)
-        );
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
-
-      testWidgets('backgroundColor - reverse', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(contentHeight: 1200.0, reverse: true)
-        );
-        await tester.pump();
-
-        // In this test case, the content always extends under the AppBar, so it
-        // should always be the scrolledColor.
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
-
-      testWidgets('backgroundColor with FlexibleSpace - reverse', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(
-            contentHeight: 1200.0,
-            reverse: true,
-            includeFlexibleSpace: true,
-          )
-        );
-        await tester.pump();
-
-        // In this test case, the content always extends under the AppBar, so it
-        // should always be the scrolledColor.
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-
-        gesture = await tester.startGesture(const Offset(50.0, 300.0));
-        await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
-
-        expect(getAppBarBackgroundColor(tester), scrolledColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
-
-      testWidgets('_handleScrollMetricsNotification safely calls setState()', (WidgetTester tester) async {
-        // Regression test for failures found in Google internal issue b/185192049.
-        final ScrollController controller = ScrollController(initialScrollOffset: 400);
-        await tester.pumpWidget(
-          MaterialApp(
-            home: Scaffold(
-              appBar: AppBar(
-                title: const Text('AppBar'),
-              ),
-              body: Scrollbar(
-                isAlwaysShown: true,
-                controller: controller,
-                child: ListView(
-                  controller: controller,
-                  children: <Widget>[
-                    Container(height: 1200.0, color: Colors.teal),
-                  ],
-                ),
-              ),
-            ),
-          ),
-        );
-
-        expect(tester.takeException(), isNull);
-      });
-
-      testWidgets('does not trigger on horizontal scroll', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          MaterialApp(
-            home: Scaffold(
-              appBar: AppBar(
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          body: CustomScrollView(
+            slivers: <Widget>[
+              SliverAppBar(
                 elevation: 0,
                 backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
-                  return states.contains(MaterialState.scrolledUnder)
-                    ? scrolledColor
-                    : defaultColor;
+                  return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
                 }),
-                title: const Text('AppBar'),
+                expandedHeight: expandedHeight,
+                pinned: true,
+                flexibleSpace: const FlexibleSpaceBar(
+                  title: Text('SliverAppBar'),
+                ),
               ),
-              body: ListView(
-                scrollDirection: Axis.horizontal,
-                children: <Widget>[
-                  Container(height: 600.0, width: 1200.0, color: Colors.teal),
-                ],
+              SliverList(
+                delegate: SliverChildListDelegate(
+                    <Widget>[
+                      Container(height: 1200.0, color: Colors.teal),
+                    ],
+                ),
               ),
+            ],
+          ),
+        ),
+      ),
+    );
+
+    Finder findAppBarMaterial() {
+      // There are 2 Material widgets below AppBar. The second is only added if
+      // flexibleSpace is non-null.
+      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
+    }
+
+    Color? getAppBarBackgroundColor() {
+      return tester.widget<Material>(findAppBarMaterial()).color;
+    }
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
+
+    TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(0.0, -expandedHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), scrolledColor);
+    expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
+
+    gesture = await tester.startGesture(const Offset(50.0, 300.0));
+    await gesture.moveBy(const Offset(0.0, expandedHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
+  });
+
+  testWidgets('AppBar.backgroundColor MaterialStateColor scrolledUnder', (WidgetTester tester) async {
+    const Color scrolledColor = Color(0xff00ff00);
+    const Color defaultColor = Color(0xff0000ff);
+
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          appBar: AppBar(
+            elevation: 0,
+            backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
+              return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
+            }),
+            title: const Text('AppBar'),
+          ),
+          body: ListView(
+            children: <Widget>[
+              Container(height: 1200.0, color: Colors.teal),
+            ],
+          ),
+        ),
+      ),
+    );
+
+    Finder findAppBarMaterial() {
+      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
+    }
+
+    Color? getAppBarBackgroundColor() {
+      return tester.widget<Material>(findAppBarMaterial()).color;
+    }
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+
+    TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), scrolledColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+
+    gesture = await tester.startGesture(const Offset(50.0, 300.0));
+    await gesture.moveBy(const Offset(0.0, kToolbarHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+  });
+
+  testWidgets('AppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
+    const Color scrolledColor = Color(0xff00ff00);
+    const Color defaultColor = Color(0xff0000ff);
+
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          appBar: AppBar(
+            elevation: 0,
+            backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
+              return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
+            }),
+            title: const Text('AppBar'),
+            flexibleSpace: const FlexibleSpaceBar(
+              title: Text('FlexibleSpace'),
             ),
           ),
-        );
+          body: ListView(
+            children: <Widget>[
+              Container(height: 1200.0, color: Colors.teal),
+            ],
+          ),
+        ),
+      ),
+    );
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
+    Finder findAppBarMaterial() {
+      // There are 2 Material widgets below AppBar. The second is only added if
+      // flexibleSpace is non-null.
+      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
+    }
 
-        TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(-100.0, 0.0));
-        await gesture.up();
-        await tester.pumpAndSettle();
+    Color? getAppBarBackgroundColor() {
+      return tester.widget<Material>(findAppBarMaterial()).color;
+    }
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
 
-        gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(100.0, 0.0));
-        await gesture.up();
-        await tester.pumpAndSettle();
+    TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-      });
+    expect(getAppBarBackgroundColor(), scrolledColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
 
-      testWidgets('backgroundColor - not triggered in reverse for short content', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(
-            contentHeight: 200.0,
-            reverse: true,
-          )
-        );
-        await tester.pump();
+    gesture = await tester.startGesture(const Offset(50.0, 300.0));
+    await gesture.moveBy(const Offset(0.0, kToolbarHeight));
+    await gesture.up();
+    await tester.pumpAndSettle();
 
-        // In reverse, the content here is not long enough to scroll under the app
-        // bar.
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+    expect(getAppBarBackgroundColor(), defaultColor);
+    expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+  });
 
-        final TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
+  testWidgets('AppBar._handleScrollNotification safely calls setState()', (WidgetTester tester) async {
+    // Regression test for failures found in Google internal issue b/185192049.
+    final ScrollController controller = ScrollController(initialScrollOffset: 400);
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          appBar: AppBar(
+            title: const Text('AppBar'),
+          ),
+          body: Scrollbar(
+            isAlwaysShown: true,
+            controller: controller,
+            child: ListView(
+              controller: controller,
+              children: <Widget>[
+                Container(height: 1200.0, color: Colors.teal),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
+    expect(tester.takeException(), isNull);
+  });
 
-      testWidgets('backgroundColor with FlexibleSpace - not triggered in reverse for short content', (WidgetTester tester) async {
-        await tester.pumpWidget(
-          _buildAppBar(
-            contentHeight: 200.0,
-            reverse: true,
-            includeFlexibleSpace: true,
-          )
-        );
-        await tester.pump();
+  testWidgets('AppBar scrolledUnder does not trigger on horizontal scroll', (WidgetTester tester) async {
+    const Color scrolledColor = Color(0xff00ff00);
+    const Color defaultColor = Color(0xff0000ff);
 
-        // In reverse, the content here is not long enough to scroll under the app
-        // bar.
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Scaffold(
+          appBar: AppBar(
+            elevation: 0,
+            backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
+              return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
+            }),
+            title: const Text('AppBar'),
+          ),
+          body: ListView(
+            scrollDirection: Axis.horizontal,
+            children: <Widget>[
+              Container(height: 600.0, width: 1200.0, color: Colors.teal),
+            ],
+          ),
+        ),
+      ),
+    );
 
-        final TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
-        await gesture.moveBy(const Offset(0.0, kToolbarHeight));
-        await gesture.up();
-        await tester.pumpAndSettle();
+    Finder findAppBarMaterial() {
+      return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
+    }
 
-        expect(getAppBarBackgroundColor(tester), defaultColor);
-        expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
-      });
-    });
+    Color? getAppBarBackgroundColor() {
+      return tester.widget<Material>(findAppBarMaterial()).color;
+    }
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+
+    TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(-100.0, 0.0));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), defaultColor);
+
+    gesture = await tester.startGesture(const Offset(50.0, 400.0));
+    await gesture.moveBy(const Offset(100.0, 0.0));
+    await gesture.up();
+    await tester.pumpAndSettle();
+
+    expect(getAppBarBackgroundColor(), defaultColor);
   });
 
   testWidgets('AppBar.preferredHeightFor', (WidgetTester tester) async {
diff --git a/packages/flutter/test/material/scaffold_test.dart b/packages/flutter/test/material/scaffold_test.dart
index 759853e..9cb32cd 100644
--- a/packages/flutter/test/material/scaffold_test.dart
+++ b/packages/flutter/test/material/scaffold_test.dart
@@ -2302,9 +2302,9 @@
       '     PhysicalModel\n'
       '     AnimatedPhysicalModel\n'
       '     Material\n'
-      '     _ScrollMetricsNotificationObserverScope\n'
-      '     NotificationListener<ScrollMetricsNotification>\n'
-      '     ScrollMetricsNotificationObserver\n'
+      '     _ScrollNotificationObserverScope\n'
+      '     NotificationListener<ScrollNotification>\n'
+      '     ScrollNotificationObserver\n'
       '     _ScaffoldScope\n'
       '     Scaffold\n'
       '     MediaQuery\n'