Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit fdc25a1

Browse files
authored
Reland "Remove single-view assumption from ScrollPhysics (#117503)" (#117916)
This reverts commit c956121.
1 parent 084be5e commit fdc25a1

16 files changed

+102
-41
lines changed

dev/integration_tests/flutter_gallery/lib/demo/animation/home.dart

+8-8
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,14 @@ class _SnappingScrollPhysics extends ClampingScrollPhysics {
371371
return _SnappingScrollPhysics(parent: buildParent(ancestor), midScrollOffset: midScrollOffset);
372372
}
373373

374-
Simulation _toMidScrollOffsetSimulation(double offset, double dragVelocity) {
374+
Simulation _toMidScrollOffsetSimulation(double offset, double dragVelocity, ScrollMetrics metrics) {
375375
final double velocity = math.max(dragVelocity, minFlingVelocity);
376-
return ScrollSpringSimulation(spring, offset, midScrollOffset, velocity, tolerance: tolerance);
376+
return ScrollSpringSimulation(spring, offset, midScrollOffset, velocity, tolerance: toleranceFor(metrics));
377377
}
378378

379-
Simulation _toZeroScrollOffsetSimulation(double offset, double dragVelocity) {
379+
Simulation _toZeroScrollOffsetSimulation(double offset, double dragVelocity, ScrollMetrics metrics) {
380380
final double velocity = math.max(dragVelocity, minFlingVelocity);
381-
return ScrollSpringSimulation(spring, offset, 0.0, velocity, tolerance: tolerance);
381+
return ScrollSpringSimulation(spring, offset, 0.0, velocity, tolerance: toleranceFor(metrics));
382382
}
383383

384384
@override
@@ -396,10 +396,10 @@ class _SnappingScrollPhysics extends ClampingScrollPhysics {
396396
return simulation;
397397
}
398398
if (dragVelocity > 0.0) {
399-
return _toMidScrollOffsetSimulation(offset, dragVelocity);
399+
return _toMidScrollOffsetSimulation(offset, dragVelocity, position);
400400
}
401401
if (dragVelocity < 0.0) {
402-
return _toZeroScrollOffsetSimulation(offset, dragVelocity);
402+
return _toZeroScrollOffsetSimulation(offset, dragVelocity, position);
403403
}
404404
} else {
405405
// The user ended the drag with little or no velocity. If they
@@ -408,10 +408,10 @@ class _SnappingScrollPhysics extends ClampingScrollPhysics {
408408
// otherwise snap to zero.
409409
final double snapThreshold = midScrollOffset / 2.0;
410410
if (offset >= snapThreshold && offset < midScrollOffset) {
411-
return _toMidScrollOffsetSimulation(offset, dragVelocity);
411+
return _toMidScrollOffsetSimulation(offset, dragVelocity, position);
412412
}
413413
if (offset > 0.0 && offset < snapThreshold) {
414-
return _toZeroScrollOffsetSimulation(offset, dragVelocity);
414+
return _toZeroScrollOffsetSimulation(offset, dragVelocity, position);
415415
}
416416
}
417417
return simulation;

packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart

+4-4
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ class _DraggableScrollableSheetScrollPosition extends ScrollPositionWithSingleCo
902902
bool get _isAtSnapSize {
903903
return extent.snapSizes.any(
904904
(double snapSize) {
905-
return (extent.currentSize - snapSize).abs() <= extent.pixelsToSize(physics.tolerance.distance);
905+
return (extent.currentSize - snapSize).abs() <= extent.pixelsToSize(physics.toleranceFor(this).distance);
906906
},
907907
);
908908
}
@@ -937,7 +937,7 @@ class _DraggableScrollableSheetScrollPosition extends ScrollPositionWithSingleCo
937937
initialVelocity: velocity,
938938
pixelSnapSize: extent.pixelSnapSizes,
939939
snapAnimationDuration: extent.snapAnimationDuration,
940-
tolerance: physics.tolerance,
940+
tolerance: physics.toleranceFor(this),
941941
);
942942
} else {
943943
// The iOS bouncing simulation just isn't right here - once we delegate
@@ -946,7 +946,7 @@ class _DraggableScrollableSheetScrollPosition extends ScrollPositionWithSingleCo
946946
// Run the simulation in terms of pixels, not extent.
947947
position: extent.currentPixels,
948948
velocity: velocity,
949-
tolerance: physics.tolerance,
949+
tolerance: physics.toleranceFor(this),
950950
);
951951
}
952952

@@ -965,7 +965,7 @@ class _DraggableScrollableSheetScrollPosition extends ScrollPositionWithSingleCo
965965
// Make sure we pass along enough velocity to keep scrolling - otherwise
966966
// we just "bounce" off the top making it look like the list doesn't
967967
// have more to scroll.
968-
velocity = ballisticController.velocity + (physics.tolerance.velocity * ballisticController.velocity.sign);
968+
velocity = ballisticController.velocity + (physics.toleranceFor(this).velocity * ballisticController.velocity.sign);
969969
super.goBallistic(velocity);
970970
ballisticController.stop();
971971
} else if (ballisticController.isCompleted) {

packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ class FixedExtentMetrics extends FixedScrollMetrics {
316316
required super.viewportDimension,
317317
required super.axisDirection,
318318
required this.itemIndex,
319+
required super.devicePixelRatio,
319320
});
320321

321322
@override
@@ -326,6 +327,7 @@ class FixedExtentMetrics extends FixedScrollMetrics {
326327
double? viewportDimension,
327328
AxisDirection? axisDirection,
328329
int? itemIndex,
330+
double? devicePixelRatio,
329331
}) {
330332
return FixedExtentMetrics(
331333
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
@@ -334,6 +336,7 @@ class FixedExtentMetrics extends FixedScrollMetrics {
334336
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
335337
axisDirection: axisDirection ?? this.axisDirection,
336338
itemIndex: itemIndex ?? this.itemIndex,
339+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
337340
);
338341
}
339342

@@ -399,6 +402,7 @@ class _FixedExtentScrollPosition extends ScrollPositionWithSingleContext impleme
399402
double? viewportDimension,
400403
AxisDirection? axisDirection,
401404
int? itemIndex,
405+
double? devicePixelRatio,
402406
}) {
403407
return FixedExtentMetrics(
404408
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
@@ -407,6 +411,7 @@ class _FixedExtentScrollPosition extends ScrollPositionWithSingleContext impleme
407411
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
408412
axisDirection: axisDirection ?? this.axisDirection,
409413
itemIndex: itemIndex ?? this.itemIndex,
414+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
410415
);
411416
}
412417
}
@@ -505,8 +510,8 @@ class FixedExtentScrollPhysics extends ScrollPhysics {
505510
// Scenario 3:
506511
// If there's no velocity and we're already at where we intend to land,
507512
// do nothing.
508-
if (velocity.abs() < tolerance.velocity
509-
&& (settlingPixels - metrics.pixels).abs() < tolerance.distance) {
513+
if (velocity.abs() < toleranceFor(position).velocity
514+
&& (settlingPixels - metrics.pixels).abs() < toleranceFor(position).distance) {
510515
return null;
511516
}
512517

@@ -519,7 +524,7 @@ class FixedExtentScrollPhysics extends ScrollPhysics {
519524
metrics.pixels,
520525
settlingPixels,
521526
velocity,
522-
tolerance: tolerance,
527+
tolerance: toleranceFor(position),
523528
);
524529
}
525530

@@ -530,7 +535,7 @@ class FixedExtentScrollPhysics extends ScrollPhysics {
530535
metrics.pixels,
531536
settlingPixels,
532537
velocity,
533-
tolerance.velocity * velocity.sign,
538+
toleranceFor(position).velocity * velocity.sign,
534539
);
535540
}
536541
}

packages/flutter/lib/src/widgets/nested_scroll_view.dart

+4
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ class _NestedScrollMetrics extends FixedScrollMetrics {
526526
required super.pixels,
527527
required super.viewportDimension,
528528
required super.axisDirection,
529+
required super.devicePixelRatio,
529530
required this.minRange,
530531
required this.maxRange,
531532
required this.correctionOffset,
@@ -538,6 +539,7 @@ class _NestedScrollMetrics extends FixedScrollMetrics {
538539
double? pixels,
539540
double? viewportDimension,
540541
AxisDirection? axisDirection,
542+
double? devicePixelRatio,
541543
double? minRange,
542544
double? maxRange,
543545
double? correctionOffset,
@@ -548,6 +550,7 @@ class _NestedScrollMetrics extends FixedScrollMetrics {
548550
pixels: pixels ?? (hasPixels ? this.pixels : null),
549551
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
550552
axisDirection: axisDirection ?? this.axisDirection,
553+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
551554
minRange: minRange ?? this.minRange,
552555
maxRange: maxRange ?? this.maxRange,
553556
correctionOffset: correctionOffset ?? this.correctionOffset,
@@ -815,6 +818,7 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
815818
minRange: minRange,
816819
maxRange: maxRange,
817820
correctionOffset: correctionOffset,
821+
devicePixelRatio: _outerPosition!.devicePixelRatio,
818822
);
819823
}
820824

packages/flutter/lib/src/widgets/page_view.dart

+6-1
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ class PageMetrics extends FixedScrollMetrics {
273273
required super.viewportDimension,
274274
required super.axisDirection,
275275
required this.viewportFraction,
276+
required super.devicePixelRatio,
276277
});
277278

278279
@override
@@ -283,6 +284,7 @@ class PageMetrics extends FixedScrollMetrics {
283284
double? viewportDimension,
284285
AxisDirection? axisDirection,
285286
double? viewportFraction,
287+
double? devicePixelRatio,
286288
}) {
287289
return PageMetrics(
288290
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
@@ -291,6 +293,7 @@ class PageMetrics extends FixedScrollMetrics {
291293
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
292294
axisDirection: axisDirection ?? this.axisDirection,
293295
viewportFraction: viewportFraction ?? this.viewportFraction,
296+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
294297
);
295298
}
296299

@@ -493,6 +496,7 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
493496
double? viewportDimension,
494497
AxisDirection? axisDirection,
495498
double? viewportFraction,
499+
double? devicePixelRatio,
496500
}) {
497501
return PageMetrics(
498502
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
@@ -501,6 +505,7 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
501505
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
502506
axisDirection: axisDirection ?? this.axisDirection,
503507
viewportFraction: viewportFraction ?? this.viewportFraction,
508+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
504509
);
505510
}
506511
}
@@ -573,7 +578,7 @@ class PageScrollPhysics extends ScrollPhysics {
573578
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
574579
return super.createBallisticSimulation(position, velocity);
575580
}
576-
final Tolerance tolerance = this.tolerance;
581+
final Tolerance tolerance = toleranceFor(position);
577582
final double target = _getTargetPixels(position, tolerance, velocity);
578583
if (target != position.pixels) {
579584
return ScrollSpringSimulation(spring, position.pixels, target, velocity, tolerance: tolerance);

packages/flutter/lib/src/widgets/scroll_metrics.dart

+10
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ mixin ScrollMetrics {
4646
double? pixels,
4747
double? viewportDimension,
4848
AxisDirection? axisDirection,
49+
double? devicePixelRatio,
4950
}) {
5051
return FixedScrollMetrics(
5152
minScrollExtent: minScrollExtent ?? (hasContentDimensions ? this.minScrollExtent : null),
5253
maxScrollExtent: maxScrollExtent ?? (hasContentDimensions ? this.maxScrollExtent : null),
5354
pixels: pixels ?? (hasPixels ? this.pixels : null),
5455
viewportDimension: viewportDimension ?? (hasViewportDimension ? this.viewportDimension : null),
5556
axisDirection: axisDirection ?? this.axisDirection,
57+
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
5658
);
5759
}
5860

@@ -124,6 +126,10 @@ mixin ScrollMetrics {
124126
/// The quantity of content conceptually "below" the viewport in the scrollable.
125127
/// This is the content below the content described by [extentInside].
126128
double get extentAfter => math.max(maxScrollExtent - pixels, 0.0);
129+
130+
/// The [FlutterView.devicePixelRatio] of the view that the [Scrollable]
131+
/// associated with this metrics object is drawn into.
132+
double get devicePixelRatio;
127133
}
128134

129135
/// An immutable snapshot of values associated with a [Scrollable] viewport.
@@ -137,6 +143,7 @@ class FixedScrollMetrics with ScrollMetrics {
137143
required double? pixels,
138144
required double? viewportDimension,
139145
required this.axisDirection,
146+
required this.devicePixelRatio,
140147
}) : _minScrollExtent = minScrollExtent,
141148
_maxScrollExtent = maxScrollExtent,
142149
_pixels = pixels,
@@ -170,6 +177,9 @@ class FixedScrollMetrics with ScrollMetrics {
170177
@override
171178
final AxisDirection axisDirection;
172179

180+
@override
181+
final double devicePixelRatio;
182+
173183
@override
174184
String toString() {
175185
return '${objectRuntimeType(this, 'FixedScrollMetrics')}(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)})';

packages/flutter/lib/src/widgets/scroll_physics.dart

+24-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:math' as math;
66

77
import 'package:flutter/foundation.dart';
88
import 'package:flutter/gestures.dart';
9+
import 'package:flutter/painting.dart' show AxisDirection;
910
import 'package:flutter/physics.dart';
1011

1112
import 'binding.dart' show WidgetsBinding;
@@ -383,16 +384,29 @@ class ScrollPhysics {
383384
/// The spring to use for ballistic simulations.
384385
SpringDescription get spring => parent?.spring ?? _kDefaultSpring;
385386

386-
/// The default accuracy to which scrolling is computed.
387-
static final Tolerance _kDefaultTolerance = Tolerance(
388-
// TODO(ianh): Handle the case of the device pixel ratio changing.
389-
// TODO(ianh): Get this from the local MediaQuery not dart:ui's window object.
390-
velocity: 1.0 / (0.050 * WidgetsBinding.instance.window.devicePixelRatio), // logical pixels per second
391-
distance: 1.0 / WidgetsBinding.instance.window.devicePixelRatio, // logical pixels
392-
);
387+
/// Deprecated. Call [toleranceFor] instead.
388+
@Deprecated(
389+
'Call toleranceFor instead. '
390+
'This feature was deprecated after v3.7.0-13.0.pre.',
391+
)
392+
Tolerance get tolerance {
393+
return toleranceFor(FixedScrollMetrics(
394+
minScrollExtent: null,
395+
maxScrollExtent: null,
396+
pixels: null,
397+
viewportDimension: null,
398+
axisDirection: AxisDirection.down,
399+
devicePixelRatio: WidgetsBinding.instance.window.devicePixelRatio,
400+
));
401+
}
393402

394403
/// The tolerance to use for ballistic simulations.
395-
Tolerance get tolerance => parent?.tolerance ?? _kDefaultTolerance;
404+
Tolerance toleranceFor(ScrollMetrics metrics) {
405+
return parent?.toleranceFor(metrics) ?? Tolerance(
406+
velocity: 1.0 / (0.050 * metrics.devicePixelRatio), // logical pixels per second
407+
distance: 1.0 / metrics.devicePixelRatio, // logical pixels
408+
);
409+
}
396410

397411
/// The minimum distance an input pointer drag must have moved to
398412
/// to be considered a scroll fling gesture.
@@ -696,7 +710,7 @@ class BouncingScrollPhysics extends ScrollPhysics {
696710

697711
@override
698712
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
699-
final Tolerance tolerance = this.tolerance;
713+
final Tolerance tolerance = toleranceFor(position);
700714
if (velocity.abs() >= tolerance.velocity || position.outOfRange) {
701715
double constantDeceleration;
702716
switch (decelerationRate) {
@@ -840,7 +854,7 @@ class ClampingScrollPhysics extends ScrollPhysics {
840854

841855
@override
842856
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
843-
final Tolerance tolerance = this.tolerance;
857+
final Tolerance tolerance = toleranceFor(position);
844858
if (position.outOfRange) {
845859
double? end;
846860
if (position.pixels > position.maxScrollExtent) {

packages/flutter/lib/src/widgets/scroll_position.dart

+5
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import 'package:flutter/scheduler.dart';
1212

1313
import 'basic.dart';
1414
import 'framework.dart';
15+
import 'media_query.dart';
1516
import 'notification_listener.dart';
1617
import 'page_storage.dart';
1718
import 'scroll_activity.dart';
1819
import 'scroll_context.dart';
1920
import 'scroll_metrics.dart';
2021
import 'scroll_notification.dart';
2122
import 'scroll_physics.dart';
23+
import 'view.dart';
2224

2325
export 'scroll_activity.dart' show ScrollHoldController;
2426

@@ -242,6 +244,9 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
242244
isScrollingNotifier.value = activity!.isScrolling;
243245
}
244246

247+
@override
248+
double get devicePixelRatio => MediaQuery.maybeDevicePixelRatioOf(context.storageContext) ?? View.of(context.storageContext).devicePixelRatio;
249+
245250
/// Update the scroll position ([pixels]) to a given pixel value.
246251
///
247252
/// This should only be called by the current [ScrollActivity], either during

packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
176176
required Duration duration,
177177
required Curve curve,
178178
}) {
179-
if (nearEqual(to, pixels, physics.tolerance.distance)) {
179+
if (nearEqual(to, pixels, physics.toleranceFor(this).distance)) {
180180
// Skip the animation, go straight to the position as we are already close.
181181
jumpTo(to);
182182
return Future<void>.value();

packages/flutter/lib/src/widgets/scrollable.dart

+6
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,12 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
727727
}
728728

729729
void _handleDragCancel() {
730+
if (_gestureDetectorKey.currentContext == null) {
731+
// The cancel was caused by the GestureDetector getting disposed, which
732+
// means we will get disposed momentarily as well and shouldn't do
733+
// any work.
734+
return;
735+
}
730736
// _hold might be null if the drag started.
731737
// _drag might be null if the drag activity ended and called _disposeDrag.
732738
assert(_hold == null || _drag == null);

packages/flutter/test/material/scrollbar_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ void main() {
148148
pixels: 0.0,
149149
viewportDimension: 100.0,
150150
axisDirection: AxisDirection.down,
151+
devicePixelRatio: tester.binding.window.devicePixelRatio,
151152
);
152153
scrollPainter!.update(metrics, AxisDirection.down);
153154

0 commit comments

Comments
 (0)