Unverified Commit ea26272e authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Revert "Fix reverse cases for App Bar scrolled under behavior (#101460)" (#101929)

merging now since it blocks flutter roll for too long
parent c579bbde
...@@ -736,24 +736,24 @@ class _AppBarState extends State<AppBar> { ...@@ -736,24 +736,24 @@ class _AppBarState extends State<AppBar> {
static const double _defaultElevation = 4.0; static const double _defaultElevation = 4.0;
static const Color _defaultShadowColor = Color(0xFF000000); static const Color _defaultShadowColor = Color(0xFF000000);
ScrollMetricsNotificationObserverState? _scrollMetricsNotificationObserver; ScrollNotificationObserverState? _scrollNotificationObserver;
bool _scrolledUnder = false; bool _scrolledUnder = false;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
if (_scrollMetricsNotificationObserver != null) if (_scrollNotificationObserver != null)
_scrollMetricsNotificationObserver!.removeListener(_handleScrollMetricsNotification); _scrollNotificationObserver!.removeListener(_handleScrollNotification);
_scrollMetricsNotificationObserver = ScrollMetricsNotificationObserver.of(context); _scrollNotificationObserver = ScrollNotificationObserver.of(context);
if (_scrollMetricsNotificationObserver != null) if (_scrollNotificationObserver != null)
_scrollMetricsNotificationObserver!.addListener(_handleScrollMetricsNotification); _scrollNotificationObserver!.addListener(_handleScrollNotification);
} }
@override @override
void dispose() { void dispose() {
if (_scrollMetricsNotificationObserver != null) { if (_scrollNotificationObserver != null) {
_scrollMetricsNotificationObserver!.removeListener(_handleScrollMetricsNotification); _scrollNotificationObserver!.removeListener(_handleScrollNotification);
_scrollMetricsNotificationObserver = null; _scrollNotificationObserver = null;
} }
super.dispose(); super.dispose();
} }
...@@ -766,34 +766,18 @@ class _AppBarState extends State<AppBar> { ...@@ -766,34 +766,18 @@ class _AppBarState extends State<AppBar> {
Scaffold.of(context).openEndDrawer(); Scaffold.of(context).openEndDrawer();
} }
void _handleScrollMetricsNotification(ScrollMetricsNotification notification) { void _handleScrollNotification(ScrollNotification notification) {
final bool oldScrolledUnder = _scrolledUnder; if (notification is ScrollUpdateNotification) {
final ScrollMetrics metrics = notification.metrics; final bool oldScrolledUnder = _scrolledUnder;
_scrolledUnder = notification.depth == 0
if (notification.depth != 0) { && notification.metrics.extentBefore > 0
_scrolledUnder = false; && notification.metrics.axis == Axis.vertical;
} else { if (_scrolledUnder != oldScrolledUnder) {
switch (metrics.axisDirection) { setState(() {
case AxisDirection.up: // React to a change in MaterialState.scrolledUnder
// 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;
} }
} }
if (_scrolledUnder != oldScrolledUnder) {
setState(() {
// React to a change in MaterialState.scrolledUnder
});
}
} }
Color _resolveColor(Set<MaterialState> states, Color? widgetColor, Color? themeColor, Color defaultColor) { Color _resolveColor(Set<MaterialState> states, Color? widgetColor, Color? themeColor, Color defaultColor) {
......
...@@ -2831,7 +2831,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto ...@@ -2831,7 +2831,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
return _ScaffoldScope( return _ScaffoldScope(
hasDrawer: hasDrawer, hasDrawer: hasDrawer,
geometryNotifier: _geometryNotifier, geometryNotifier: _geometryNotifier,
child: ScrollMetricsNotificationObserver( child: ScrollNotificationObserver(
child: Material( child: Material(
color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor, color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) { child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
......
...@@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart'; ...@@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart';
import 'framework.dart'; import 'framework.dart';
import 'notification_listener.dart'; import 'notification_listener.dart';
import 'scroll_notification.dart'; import 'scroll_notification.dart';
import 'scroll_position.dart';
/// A [ScrollNotification] listener for [ScrollNotificationObserver]. /// A [ScrollNotification] listener for [ScrollNotificationObserver].
/// ///
...@@ -171,168 +170,3 @@ class ScrollNotificationObserverState extends State<ScrollNotificationObserver> ...@@ -171,168 +170,3 @@ class ScrollNotificationObserverState extends State<ScrollNotificationObserver>
super.dispose(); 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({
required super.child,
required ScrollMetricsNotificationObserverState scrollMetricsNotificationObserverState,
}) : _scrollMetricsNotificationObserverState = scrollMetricsNotificationObserverState;
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({
super.key,
required this.child,
}) : assert(child != null);
/// 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();
}
}
...@@ -2567,495 +2567,311 @@ void main() { ...@@ -2567,495 +2567,311 @@ void main() {
expect(actionIconTheme.color, foregroundColor); expect(actionIconTheme.color, foregroundColor);
}); });
group('MaterialStateColor scrolledUnder', () { testWidgets('SliverAppBar.backgroundColor MaterialStateColor scrolledUnder', (WidgetTester tester) async {
const double collapsedHeight = kToolbarHeight; const double collapsedHeight = kToolbarHeight;
const double expandedHeight = 200.0; const double expandedHeight = 200.0;
const Color scrolledColor = Color(0xff00ff00); const Color scrolledColor = Color(0xff00ff00);
const Color defaultColor = Color(0xff0000ff); 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() { 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; return tester.widget<Material>(findAppBarMaterial()).color;
} }
group('SliverAppBar', () { expect(getAppBarBackgroundColor(), defaultColor);
Widget _buildSliverApp({ expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
required double contentHeight,
bool reverse = false, TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
bool includeFlexibleSpace = false, await gesture.moveBy(const Offset(0.0, -expandedHeight));
}) { await gesture.up();
return MaterialApp( await tester.pumpAndSettle();
home: Scaffold(
body: CustomScrollView( expect(getAppBarBackgroundColor(), scrolledColor);
reverse: reverse, expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
slivers: <Widget>[
SliverAppBar( gesture = await tester.startGesture(const Offset(50.0, 300.0));
elevation: 0, await gesture.moveBy(const Offset(0.0, expandedHeight));
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) { await gesture.up();
return states.contains(MaterialState.scrolledUnder) await tester.pumpAndSettle();
? scrolledColor
: defaultColor; expect(getAppBarBackgroundColor(), defaultColor);
}), expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
expandedHeight: expandedHeight, });
pinned: true,
flexibleSpace: includeFlexibleSpace testWidgets('SliverAppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
? const FlexibleSpaceBar(title: Text('SliverAppBar')) const double collapsedHeight = kToolbarHeight;
: null, 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,
flexibleSpace: const FlexibleSpaceBar(
title: Text('SliverAppBar'),
), ),
SliverList( ),
delegate: SliverChildListDelegate( SliverList(
delegate: SliverChildListDelegate(
<Widget>[ <Widget>[
Container(height: contentHeight, color: Colors.teal), Container(height: 1200.0, color: Colors.teal),
], ],
),
), ),
], ),
), ],
), ),
); ),
} ),
);
testWidgets('backgroundColor', (WidgetTester tester) async {
await tester.pumpWidget(
_buildSliverApp(contentHeight: 1200.0)
);
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', (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', () { Finder findAppBarMaterial() {
Widget _buildAppBar({ // There are 2 Material widgets below AppBar. The second is only added if
required double contentHeight, // flexibleSpace is non-null.
bool reverse = false, return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
bool includeFlexibleSpace = false }
}) {
return MaterialApp( Color? getAppBarBackgroundColor() {
home: Scaffold( return tester.widget<Material>(findAppBarMaterial()).color;
appBar: AppBar( }
elevation: 0,
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) { expect(getAppBarBackgroundColor(), defaultColor);
return states.contains(MaterialState.scrolledUnder) expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
? scrolledColor
: defaultColor; TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
}), await gesture.moveBy(const Offset(0.0, -expandedHeight));
title: const Text('AppBar'), await gesture.up();
flexibleSpace: includeFlexibleSpace await tester.pumpAndSettle();
? const FlexibleSpaceBar(title: Text('FlexibleSpace'))
: null, expect(getAppBarBackgroundColor(), scrolledColor);
), expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
body: ListView(
reverse: reverse, gesture = await tester.startGesture(const Offset(50.0, 300.0));
children: <Widget>[ await gesture.moveBy(const Offset(0.0, expandedHeight));
Container(height: contentHeight, color: Colors.teal), 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),
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);
});
// Regression test for https://github.com/flutter/flutter/issues/80256
testWidgets('The second page should have a back button even it has a end drawer', (WidgetTester tester) async {
final Page<void> page1 = MaterialPage<void>(
key: const ValueKey<String>('1'),
child: Scaffold(
key: const ValueKey<String>('1'),
appBar: AppBar(),
endDrawer: const Drawer(),
)
);
final Page<void> page2 = MaterialPage<void>(
key: const ValueKey<String>('2'),
child: Scaffold(
key: const ValueKey<String>('2'),
appBar: AppBar(),
endDrawer: const Drawer(),
)
);
final List<Page<void>> pages = <Page<void>>[ page1, page2 ];
await tester.pumpWidget(
MaterialApp(
home: Navigator(
pages: pages,
onPopPage: (Route<Object?> route, Object? result) => false,
),
), ),
); ),
),
);
// The page2 should have a back button. Finder findAppBarMaterial() {
expect( return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
find.descendant( }
of: find.byKey(const ValueKey<String>('2')),
matching: find.byType(BackButton), Color? getAppBarBackgroundColor() {
), return tester.widget<Material>(findAppBarMaterial()).color;
findsOneWidget }
);
}); expect(getAppBarBackgroundColor(), defaultColor);
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
testWidgets('backgroundColor with FlexibleSpace - reverse', (WidgetTester tester) async {
await tester.pumpWidget( TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
_buildAppBar( await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
contentHeight: 1200.0, await gesture.up();
reverse: true, await tester.pumpAndSettle();
includeFlexibleSpace: true,
) expect(getAppBarBackgroundColor(), scrolledColor);
); expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
await tester.pump();
gesture = await tester.startGesture(const Offset(50.0, 300.0));
// In this test case, the content always extends under the AppBar, so it await gesture.moveBy(const Offset(0.0, kToolbarHeight));
// should always be the scrolledColor. await gesture.up();
expect(getAppBarBackgroundColor(tester), scrolledColor); await tester.pumpAndSettle();
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
expect(getAppBarBackgroundColor(), defaultColor);
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0)); expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
await gesture.moveBy(const Offset(0.0, kToolbarHeight)); });
await gesture.up();
await tester.pumpAndSettle(); testWidgets('AppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
const Color scrolledColor = Color(0xff00ff00);
expect(getAppBarBackgroundColor(tester), scrolledColor); const Color defaultColor = Color(0xff0000ff);
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
await tester.pumpWidget(
gesture = await tester.startGesture(const Offset(50.0, 300.0)); MaterialApp(
await gesture.moveBy(const Offset(0.0, -kToolbarHeight)); home: Scaffold(
await gesture.up(); appBar: AppBar(
await tester.pumpAndSettle(); elevation: 0,
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
expect(getAppBarBackgroundColor(tester), scrolledColor); return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight); }),
}); title: const Text('AppBar'),
flexibleSpace: const FlexibleSpaceBar(
testWidgets('_handleScrollMetricsNotification safely calls setState()', (WidgetTester tester) async { title: Text('FlexibleSpace'),
// 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),
],
),
),
), ),
), ),
); body: ListView(
children: <Widget>[
Container(height: 1200.0, color: Colors.teal),
],
),
),
),
);
expect(tester.takeException(), isNull); 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;
}
testWidgets('does not trigger on horizontal scroll', (WidgetTester tester) async { Color? getAppBarBackgroundColor() {
await tester.pumpWidget( return tester.widget<Material>(findAppBarMaterial()).color;
MaterialApp( }
home: Scaffold(
appBar: AppBar( expect(getAppBarBackgroundColor(), defaultColor);
elevation: 0, expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
return states.contains(MaterialState.scrolledUnder) TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
? scrolledColor await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
: defaultColor; await gesture.up();
}), await tester.pumpAndSettle();
title: const Text('AppBar'),
), expect(getAppBarBackgroundColor(), scrolledColor);
body: ListView( expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
scrollDirection: Axis.horizontal,
children: <Widget>[ gesture = await tester.startGesture(const Offset(50.0, 300.0));
Container(height: 600.0, width: 1200.0, color: Colors.teal), 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._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); );
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0)); expect(tester.takeException(), isNull);
await gesture.moveBy(const Offset(-100.0, 0.0)); });
await gesture.up();
await tester.pumpAndSettle(); testWidgets('AppBar scrolledUnder does not trigger on horizontal scroll', (WidgetTester tester) async {
const Color scrolledColor = Color(0xff00ff00);
expect(getAppBarBackgroundColor(tester), defaultColor); const Color defaultColor = Color(0xff0000ff);
gesture = await tester.startGesture(const Offset(50.0, 400.0)); await tester.pumpWidget(
await gesture.moveBy(const Offset(100.0, 0.0)); MaterialApp(
await gesture.up(); home: Scaffold(
await tester.pumpAndSettle(); appBar: AppBar(
elevation: 0,
expect(getAppBarBackgroundColor(tester), defaultColor); backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
}); return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
}),
testWidgets('backgroundColor - not triggered in reverse for short content', (WidgetTester tester) async { title: const Text('AppBar'),
await tester.pumpWidget( ),
_buildAppBar( body: ListView(
contentHeight: 200.0, scrollDirection: Axis.horizontal,
reverse: true, children: <Widget>[
) Container(height: 600.0, width: 1200.0, color: Colors.teal),
); ],
await tester.pump(); ),
),
// 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); Finder findAppBarMaterial() {
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
final TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0)); }
await gesture.moveBy(const Offset(0.0, kToolbarHeight));
await gesture.up(); Color? getAppBarBackgroundColor() {
await tester.pumpAndSettle(); return tester.widget<Material>(findAppBarMaterial()).color;
}
expect(getAppBarBackgroundColor(tester), defaultColor);
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight); expect(getAppBarBackgroundColor(), defaultColor);
});
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
testWidgets('backgroundColor with FlexibleSpace - not triggered in reverse for short content', (WidgetTester tester) async { await gesture.moveBy(const Offset(-100.0, 0.0));
await tester.pumpWidget( await gesture.up();
_buildAppBar( await tester.pumpAndSettle();
contentHeight: 200.0,
reverse: true, expect(getAppBarBackgroundColor(), defaultColor);
includeFlexibleSpace: true,
) gesture = await tester.startGesture(const Offset(50.0, 400.0));
); await gesture.moveBy(const Offset(100.0, 0.0));
await tester.pump(); await gesture.up();
await tester.pumpAndSettle();
// In reverse, the content here is not long enough to scroll under the app
// bar. expect(getAppBarBackgroundColor(), defaultColor);
expect(getAppBarBackgroundColor(tester), 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();
expect(getAppBarBackgroundColor(tester), defaultColor);
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
});
});
}); });
testWidgets('AppBar.preferredHeightFor', (WidgetTester tester) async { testWidgets('AppBar.preferredHeightFor', (WidgetTester tester) async {
......
...@@ -344,9 +344,9 @@ void main() { ...@@ -344,9 +344,9 @@ void main() {
' PhysicalModel\n' ' PhysicalModel\n'
' AnimatedPhysicalModel\n' ' AnimatedPhysicalModel\n'
' Material\n' ' Material\n'
' _ScrollMetricsNotificationObserverScope\n' ' _ScrollNotificationObserverScope\n'
' NotificationListener<ScrollMetricsNotification>\n' ' NotificationListener<ScrollNotification>\n'
' ScrollMetricsNotificationObserver\n' ' ScrollNotificationObserver\n'
' _ScaffoldScope\n' ' _ScaffoldScope\n'
' Scaffold-[LabeledGlobalKey<ScaffoldState>#00000]\n' ' Scaffold-[LabeledGlobalKey<ScaffoldState>#00000]\n'
' MediaQuery\n' ' MediaQuery\n'
......
...@@ -2302,9 +2302,9 @@ void main() { ...@@ -2302,9 +2302,9 @@ void main() {
' PhysicalModel\n' ' PhysicalModel\n'
' AnimatedPhysicalModel\n' ' AnimatedPhysicalModel\n'
' Material\n' ' Material\n'
' _ScrollMetricsNotificationObserverScope\n' ' _ScrollNotificationObserverScope\n'
' NotificationListener<ScrollMetricsNotification>\n' ' NotificationListener<ScrollNotification>\n'
' ScrollMetricsNotificationObserver\n' ' ScrollNotificationObserver\n'
' _ScaffoldScope\n' ' _ScaffoldScope\n'
' Scaffold\n' ' Scaffold\n'
' MediaQuery\n' ' MediaQuery\n'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment