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

Correct scroll notifications for NestedScrollView (#96482)

parent 4575a69d
...@@ -890,6 +890,9 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont ...@@ -890,6 +890,9 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
} }
void pointerScroll(double delta) { void pointerScroll(double delta) {
// If an update is made to pointer scrolling here, consider if the same
// (or similar) change should be made in
// ScrollPositionWithSingleContext.pointerScroll.
assert(delta != 0.0); assert(delta != 0.0);
goIdle(); goIdle();
...@@ -897,12 +900,15 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont ...@@ -897,12 +900,15 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
delta < 0.0 ? ScrollDirection.forward : ScrollDirection.reverse, delta < 0.0 ? ScrollDirection.forward : ScrollDirection.reverse,
); );
// Set the isScrollingNotifier. Even if only one position actually receives // Handle notifications. Even if only one position actually receives
// the delta, the NestedScrollView's intention is to treat multiple // the delta, the NestedScrollView's intention is to treat multiple
// ScrollPositions as one. // ScrollPositions as one.
_outerPosition!.isScrollingNotifier.value = true; _outerPosition!.isScrollingNotifier.value = true;
for (final _NestedScrollPosition position in _innerPositions) _outerPosition!.didStartScroll();
for (final _NestedScrollPosition position in _innerPositions) {
position.isScrollingNotifier.value = true; position.isScrollingNotifier.value = true;
position.didStartScroll();
}
if (_innerPositions.isEmpty) { if (_innerPositions.isEmpty) {
// Does not enter overscroll. // Does not enter overscroll.
...@@ -950,6 +956,11 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont ...@@ -950,6 +956,11 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
_outerPosition!.applyClampedPointerSignalUpdate(outerDelta); _outerPosition!.applyClampedPointerSignalUpdate(outerDelta);
} }
} }
_outerPosition!.didEndScroll();
for (final _NestedScrollPosition position in _innerPositions) {
position.didEndScroll();
}
goBallistic(0.0); goBallistic(0.0);
} }
......
...@@ -205,6 +205,9 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc ...@@ -205,6 +205,9 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
@override @override
void pointerScroll(double delta) { void pointerScroll(double delta) {
// If an update is made to pointer scrolling here, consider if the same
// (or similar) change should be made in
// _NestedScrollCoordinator.pointerScroll.
assert(delta != 0.0); assert(delta != 0.0);
final double targetPixels = final double targetPixels =
......
...@@ -2491,6 +2491,68 @@ void main() { ...@@ -2491,6 +2491,68 @@ void main() {
await tester.fling(find.text('Item 25'), const Offset(0.0, -50.0), 4000.0); await tester.fling(find.text('Item 25'), const Offset(0.0, -50.0), 4000.0);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('NestedScrollViewCoordinator.pointerScroll dispatches correct scroll notifications', (WidgetTester tester) async {
int scrollEnded = 0;
int scrollStarted = 0;
bool isScrolled = false;
await tester.pumpWidget(MaterialApp(
home: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification is ScrollStartNotification) {
scrollStarted += 1;
} else if (notification is ScrollEndNotification) {
scrollEnded += 1;
}
return false;
},
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
isScrolled = innerBoxIsScrolled;
return <Widget>[
const SliverAppBar(
expandedHeight: 250.0,
),
];
},
body: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverFixedExtentList(
itemExtent: 48.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount: 30,
),
),
),
],
),
),
),
),
));
final Offset scrollEventLocation = tester.getCenter(find.byType(NestedScrollView));
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
// Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer.hover(scrollEventLocation);
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 300.0)));
await tester.pumpAndSettle();
expect(isScrolled, isTrue);
// There should have been a notification for each nested position (2).
expect(scrollStarted, 2);
expect(scrollEnded, 2);
});
} }
class TestHeader extends SliverPersistentHeaderDelegate { class TestHeader extends SliverPersistentHeaderDelegate {
......
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