Skip to content

Commit

Permalink
Merge branch 'main' into refine-ballistic
Browse files Browse the repository at this point in the history
  • Loading branch information
fujidaiti authored Sep 14, 2024
2 parents 935117d + 65724e7 commit 9a67aa4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 29 deletions.
12 changes: 12 additions & 0 deletions lib/src/internal/float_comp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,16 @@ class FloatComp {

/// Returns [b] if [a] is approximately equal to [b], otherwise [a].
double roundToIfApprox(double a, double b) => isApprox(a, b) ? b : a;

/// Rounds the given values to the nearest edge value if
/// they are approximately equal.
///
/// If `a` is approximately equal to `b`, returns `b`.
/// If `a` is approximately equal to `c`, returns `c`.
/// Otherwise, returns `a`.
double roundToEdgeIfApprox(double a, double b, double c) {
if (isApprox(a, b)) return b;
if (isApprox(a, c)) return c;
return a;
}
}
13 changes: 7 additions & 6 deletions lib/src/scrollable/scrollable_sheet_extent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,18 +208,19 @@ class ScrollableSheetExtent extends SheetExtent
draggableDistance + scrollableDistance;
final scrollMetricsForScrollPhysics = scrollPosition.copyWith(
minScrollExtent: 0,
// How many pixels the user can scroll and drag
// How many pixels the user can scroll and drag.
maxScrollExtent: maxScrollExtentForScrollPhysics,
// How many pixels the user has scrolled and dragged
pixels: FloatComp.distance(context.devicePixelRatio).roundToIfApprox(
// Round the scrollPixelsForScrollPhysics to the maxScrollExtent if
// necessary to prevents issues with floating-point precision errors.
// For example, issue #207 was caused by infinite recursion of
// How many pixels the user has scrolled and dragged.
pixels: FloatComp.distance(context.devicePixelRatio).roundToEdgeIfApprox(
// Round the scrollPixelsForScrollPhysics to 0.0 or the maxScrollExtent
// if necessary to prevents issues with floating-point precision errors.
// For example, issue #207 and #212 were caused by infinite recursion of
// SheetContentScrollPositionOwner.goBallisticWithScrollPosition calls,
// triggered by ScrollMetrics.outOfRange always being true in
// ScrollPhysics.createBallisticSimulation due to such a floating-point
// precision error.
scrollPixelsForScrollPhysics,
0,
maxScrollExtentForScrollPhysics,
),
);
Expand Down
71 changes: 48 additions & 23 deletions test/scrollable/scrollable_sheet_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,16 @@ void main() {
});
});

// Regression test for https://github.com/fujidaiti/smooth_sheets/issues/207
testWidgets('Infinite ballistic scroll activity test', (tester) async {
// Regression tests for:
// - https://github.com/fujidaiti/smooth_sheets/issues/207
// - https://github.com/fujidaiti/smooth_sheets/issues/212
group('Infinite ballistic scroll activity test', () {
late ScrollController scrollController;
late ScrollableSheetExtent sheetExtent;
late Widget testWidget;

await tester.pumpWidget(
ScrollableSheet(
setUp(() {
testWidget = ScrollableSheet(
child: Builder(
builder: (context) {
scrollController = PrimaryScrollController.of(context);
Expand All @@ -360,25 +363,47 @@ void main() {
);
},
),
),
);
);
});

scrollController.jumpTo(600.0);
await tester.pumpAndSettle();
expect(scrollController.position.extentAfter, 0,
reason: 'Ensure that the scroll view cannot be scrolled anymore');

// Start a ballistic animation from a position extremely close to,
// but not equal, to the current position.
scrollController.position.correctPixels(600.000000001);
sheetExtent.goBallisticWithScrollPosition(
velocity: 0,
scrollPosition: scrollController.position as SheetContentScrollPosition,
);
await tester.pumpAndSettle();
expect(scrollController.position.pixels, 600.0);
expect(sheetExtent.activity, isA<IdleSheetActivity>(),
reason: 'Should not enter an infinite recursion '
'of BallisticScrollDrivenSheetActivity');
testWidgets('top edge', (tester) async {
await tester.pumpWidget(testWidget);
await tester.pumpAndSettle();
expect(scrollController.position.pixels, 0);

// Start a ballistic animation from a position extremely close to,
// but not equal, to the initial position.
scrollController.position.correctPixels(-0.000000001);
sheetExtent.goBallisticWithScrollPosition(
velocity: 0,
scrollPosition: scrollController.position as SheetContentScrollPosition,
);
await tester.pumpAndSettle();
expect(scrollController.position.pixels, 0);
expect(sheetExtent.activity, isA<IdleSheetActivity>(),
reason: 'Should not enter an infinite recursion '
'of BallisticScrollDrivenSheetActivity');
});

testWidgets('bottom edge', (tester) async {
await tester.pumpWidget(testWidget);
scrollController.jumpTo(600.0);
await tester.pumpAndSettle();
expect(scrollController.position.extentAfter, 0,
reason: 'Ensure that the scroll view cannot be scrolled anymore');

// Start a ballistic animation from a position extremely close to,
// but not equal, to the current position.
scrollController.position.correctPixels(600.000000001);
sheetExtent.goBallisticWithScrollPosition(
velocity: 0,
scrollPosition: scrollController.position as SheetContentScrollPosition,
);
await tester.pumpAndSettle();
expect(scrollController.position.pixels, 600.0);
expect(sheetExtent.activity, isA<IdleSheetActivity>(),
reason: 'Should not enter an infinite recursion '
'of BallisticScrollDrivenSheetActivity');
});
});
}

0 comments on commit 9a67aa4

Please sign in to comment.