Fix overscroll edge case that puts NestedScrollViews out of sync (#68644)
This commit is contained in:
parent
c5c2b24a07
commit
8e8f61856a
@ -1343,7 +1343,9 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
|
||||
// The logic for max is equivalent but on the other side.
|
||||
final double max = delta > 0.0
|
||||
? double.infinity
|
||||
: math.max(maxScrollExtent, pixels);
|
||||
// If pixels < 0.0, then we are currently in overscroll. The max should be
|
||||
// 0.0, representing the end of the overscrolled portion.
|
||||
: pixels < 0.0 ? 0.0 : math.max(maxScrollExtent, pixels);
|
||||
final double oldPixels = pixels;
|
||||
final double newPixels = (pixels - delta).clamp(min, max);
|
||||
final double clampedDelta = newPixels - pixels;
|
||||
|
@ -1906,6 +1906,61 @@ void main() {
|
||||
expect(tester.getCenter(find.text('Item 49')).dy, equals(585.0));
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/63978
|
||||
testWidgets('Inner _NestedScrollPosition.applyClampedDragUpdate correctly calculates range when in overscroll', (WidgetTester tester) async {
|
||||
final GlobalKey<NestedScrollViewState> nestedScrollView = GlobalKey();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: NestedScrollView(
|
||||
key: nestedScrollView,
|
||||
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
|
||||
return <Widget>[
|
||||
const SliverAppBar(
|
||||
expandedHeight: 200,
|
||||
title: Text('Test'),
|
||||
)
|
||||
];
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemExtent: 100.0,
|
||||
itemBuilder: (BuildContext context, int index) => Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Material(
|
||||
color: index.isEven ? Colors.cyan : Colors.deepOrange,
|
||||
child: Center(
|
||||
child: Text(index.toString()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(nestedScrollView.currentState!.outerController.position.pixels, 0.0);
|
||||
expect(nestedScrollView.currentState!.innerController.position.pixels, 0.0);
|
||||
expect(nestedScrollView.currentState!.outerController.position.maxScrollExtent, 200.0);
|
||||
final Offset point = tester.getCenter(find.text('1'));
|
||||
// Drag slightly into overscroll in the inner position.
|
||||
final TestGesture gesture = await tester.startGesture(point);
|
||||
await gesture.moveBy(const Offset(0.0, 5.0));
|
||||
await tester.pump();
|
||||
expect(nestedScrollView.currentState!.outerController.position.pixels, 0.0);
|
||||
expect(nestedScrollView.currentState!.innerController.position.pixels, -5.0);
|
||||
// Move by a much larger delta than the amount of over scroll, in a very
|
||||
// short period of time.
|
||||
await gesture.moveBy(const Offset(0.0, -500.0));
|
||||
await tester.pump();
|
||||
// The overscrolled inner position should have closed, then passed the
|
||||
// correct remaining delta to the outer position, and finally any remainder
|
||||
// back to the inner position.
|
||||
expect(
|
||||
nestedScrollView.currentState!.outerController.position.pixels,
|
||||
nestedScrollView.currentState!.outerController.position.maxScrollExtent,
|
||||
);
|
||||
expect(nestedScrollView.currentState!.innerController.position.pixels, 295.0);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
}
|
||||
|
||||
class TestHeader extends SliverPersistentHeaderDelegate {
|
||||
|
Loading…
x
Reference in New Issue
Block a user