Revert Ballistic & Clamping simulation updates (#111201)
This commit is contained in:
parent
dc6ab62696
commit
5f2d730f1f
@ -1,61 +0,0 @@
|
|||||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
const int _nbSamples = 100;
|
|
||||||
final List<double> _splinePosition = List<double>.filled(_nbSamples + 1, 0.0);
|
|
||||||
final List<double> _splineTime = List<double>.filled(_nbSamples + 1, 0.0);
|
|
||||||
const double _startTension = 0.5;
|
|
||||||
const double _endTension = 1.0;
|
|
||||||
const double _inflexion = 0.35;
|
|
||||||
|
|
||||||
// Generate the spline data used in ClampingScrollSimulation.
|
|
||||||
//
|
|
||||||
// This logic is a translation of the 2-dimensional logic found in
|
|
||||||
// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/Scroller.java.
|
|
||||||
//
|
|
||||||
// The output of this program should be copied over to [_splinePosition] in
|
|
||||||
// flutter/packages/flutter/lib/src/widgets/scroll_simulation.dart.
|
|
||||||
void main() {
|
|
||||||
const double p1 = _startTension * _inflexion;
|
|
||||||
const double p2 = 1.0 - _endTension * (1.0 - _inflexion);
|
|
||||||
double xMin = 0.0;
|
|
||||||
double yMin = 0.0;
|
|
||||||
for (int i = 0; i < _nbSamples; i++) {
|
|
||||||
final double alpha = i / _nbSamples;
|
|
||||||
double xMax = 1.0;
|
|
||||||
double x, tx, coef;
|
|
||||||
while (true) {
|
|
||||||
x = xMin + (xMax - xMin) / 2.0;
|
|
||||||
coef = 3.0 * x * (1.0 - x);
|
|
||||||
tx = coef * ((1.0 - x) * p1 + x * p2) + x * x * x;
|
|
||||||
if ((tx - alpha).abs() < 1e-5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (tx > alpha) {
|
|
||||||
xMax = x;
|
|
||||||
} else {
|
|
||||||
xMin = x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_splinePosition[i] = coef * ((1.0 - x) * _startTension + x) + x * x * x;
|
|
||||||
double yMax = 1.0;
|
|
||||||
double y, dy;
|
|
||||||
while (true) {
|
|
||||||
y = yMin + (yMax - yMin) / 2.0;
|
|
||||||
coef = 3.0 * y * (1.0 - y);
|
|
||||||
dy = coef * ((1.0 - y) * _startTension + y) + y * y * y;
|
|
||||||
if ((dy - alpha).abs() < 1e-5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dy > alpha) {
|
|
||||||
yMax = y;
|
|
||||||
} else {
|
|
||||||
yMin = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_splineTime[i] = coef * ((1.0 - y) * p1 + y * p2) + y * y * y;
|
|
||||||
}
|
|
||||||
_splinePosition[_nbSamples] = _splineTime[_nbSamples] = 1.0;
|
|
||||||
print(_splinePosition);
|
|
||||||
}
|
|
@ -757,15 +757,6 @@ class AnimationController extends Animation<double>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the simulation without restarting the animation.
|
|
||||||
///
|
|
||||||
/// The current simulation will be replaced with the provided [Simulation].
|
|
||||||
/// It is only valid to call this when an animation is currently underway.
|
|
||||||
void updateSimulation(Simulation simulation) {
|
|
||||||
assert(isAnimating);
|
|
||||||
_simulation = simulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops running this animation.
|
/// Stops running this animation.
|
||||||
///
|
///
|
||||||
/// This does not trigger any notifications. The animation stops in its
|
/// This does not trigger any notifications. The animation stops in its
|
||||||
|
@ -686,12 +686,6 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Piinks): https://github.com/flutter/flutter/issues/100748
|
|
||||||
@override
|
|
||||||
Simulation? updateBallisticAnimation(double initVelocity, double initPosition) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollActivity createOuterBallisticScrollActivity(double velocity) {
|
ScrollActivity createOuterBallisticScrollActivity(double velocity) {
|
||||||
// This function creates a ballistic scroll for the outer scrollable.
|
// This function creates a ballistic scroll for the outer scrollable.
|
||||||
//
|
//
|
||||||
@ -1367,12 +1361,6 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Piinks): see https://github.com/flutter/flutter/issues/100748
|
|
||||||
@override
|
|
||||||
Simulation? updateBallisticAnimation(double initVelocity, double initPosition) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollActivity createBallisticScrollActivity(
|
ScrollActivity createBallisticScrollActivity(
|
||||||
Simulation? simulation, {
|
Simulation? simulation, {
|
||||||
required _NestedBallisticScrollActivityMode mode,
|
required _NestedBallisticScrollActivityMode mode,
|
||||||
|
@ -47,16 +47,6 @@ abstract class ScrollActivityDelegate {
|
|||||||
/// Terminate the current activity and start a ballistic activity with the
|
/// Terminate the current activity and start a ballistic activity with the
|
||||||
/// given velocity.
|
/// given velocity.
|
||||||
void goBallistic(double velocity);
|
void goBallistic(double velocity);
|
||||||
|
|
||||||
/// Update the ballistic animation instead of restarting it, for example as
|
|
||||||
/// the result of a layout change after a flinging gesture.
|
|
||||||
///
|
|
||||||
/// The [initVelocity] and [initPosition] refer to the starting values of the
|
|
||||||
/// new ballistic animation.
|
|
||||||
///
|
|
||||||
/// Can return null if the current [ScrollPhysics.createBallisticSimulation]
|
|
||||||
/// returns null, which will trigger an [IdleScrollActivity] instead.
|
|
||||||
Simulation? updateBallisticAnimation(double initVelocity, double initPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base class for scrolling activities like dragging and flinging.
|
/// Base class for scrolling activities like dragging and flinging.
|
||||||
@ -534,17 +524,13 @@ class DragScrollActivity extends ScrollActivity {
|
|||||||
class BallisticScrollActivity extends ScrollActivity {
|
class BallisticScrollActivity extends ScrollActivity {
|
||||||
/// Creates an activity that animates a scroll view based on a [simulation].
|
/// Creates an activity that animates a scroll view based on a [simulation].
|
||||||
///
|
///
|
||||||
/// The [delegate], [simulation], and [vsync] arguments must not be null. The
|
/// The [delegate], [simulation], and [vsync] arguments must not be null.
|
||||||
/// [initVelocity] and [initPosition] arguments allow the ballistic activity
|
|
||||||
/// to update the simulation instead of restarting it.
|
|
||||||
BallisticScrollActivity(
|
BallisticScrollActivity(
|
||||||
super.delegate,
|
super.delegate,
|
||||||
Simulation simulation,
|
Simulation simulation,
|
||||||
TickerProvider vsync,
|
TickerProvider vsync,
|
||||||
this.shouldIgnorePointer, {
|
this.shouldIgnorePointer,
|
||||||
double initVelocity = 0.0,
|
) {
|
||||||
double initPosition = 0.0,
|
|
||||||
}) : _initVelocity = initVelocity, _initPosition = initPosition {
|
|
||||||
_controller = AnimationController.unbounded(
|
_controller = AnimationController.unbounded(
|
||||||
debugLabel: kDebugMode ? objectRuntimeType(this, 'BallisticScrollActivity') : null,
|
debugLabel: kDebugMode ? objectRuntimeType(this, 'BallisticScrollActivity') : null,
|
||||||
vsync: vsync,
|
vsync: vsync,
|
||||||
@ -556,10 +542,6 @@ class BallisticScrollActivity extends ScrollActivity {
|
|||||||
|
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
|
|
||||||
final double _initVelocity;
|
|
||||||
|
|
||||||
final double _initPosition;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void resetActivity() {
|
void resetActivity() {
|
||||||
delegate.goBallistic(velocity);
|
delegate.goBallistic(velocity);
|
||||||
@ -567,13 +549,7 @@ class BallisticScrollActivity extends ScrollActivity {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void applyNewDimensions() {
|
void applyNewDimensions() {
|
||||||
final Simulation? newSimulation = delegate.updateBallisticAnimation(
|
delegate.goBallistic(velocity);
|
||||||
_initVelocity,
|
|
||||||
_initPosition,
|
|
||||||
);
|
|
||||||
if (newSimulation != null) {
|
|
||||||
_controller.updateSimulation(newSimulation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _tick() {
|
void _tick() {
|
||||||
|
@ -12,7 +12,6 @@ import 'basic.dart';
|
|||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
import 'scroll_activity.dart';
|
import 'scroll_activity.dart';
|
||||||
import 'scroll_context.dart';
|
import 'scroll_context.dart';
|
||||||
import 'scroll_metrics.dart';
|
|
||||||
import 'scroll_notification.dart';
|
import 'scroll_notification.dart';
|
||||||
import 'scroll_physics.dart';
|
import 'scroll_physics.dart';
|
||||||
import 'scroll_position.dart';
|
import 'scroll_position.dart';
|
||||||
@ -147,35 +146,12 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
|
|||||||
simulation,
|
simulation,
|
||||||
context.vsync,
|
context.vsync,
|
||||||
activity?.shouldIgnorePointer ?? true,
|
activity?.shouldIgnorePointer ?? true,
|
||||||
initVelocity: velocity,
|
|
||||||
initPosition: pixels,
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
goIdle();
|
goIdle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Simulation? updateBallisticAnimation(double initVelocity, double initPosition) {
|
|
||||||
assert(hasPixels);
|
|
||||||
final FixedScrollMetrics initScrollMetrics = FixedScrollMetrics(
|
|
||||||
minScrollExtent: minScrollExtent,
|
|
||||||
maxScrollExtent: maxScrollExtent,
|
|
||||||
pixels: initPosition,
|
|
||||||
viewportDimension: viewportDimension,
|
|
||||||
axisDirection: axisDirection,
|
|
||||||
);
|
|
||||||
final Simulation? simulation = physics.createBallisticSimulation(
|
|
||||||
initScrollMetrics,
|
|
||||||
initVelocity,
|
|
||||||
);
|
|
||||||
if (simulation == null) {
|
|
||||||
goIdle();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return simulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollDirection get userScrollDirection => _userScrollDirection;
|
ScrollDirection get userScrollDirection => _userScrollDirection;
|
||||||
ScrollDirection _userScrollDirection = ScrollDirection.idle;
|
ScrollDirection _userScrollDirection = ScrollDirection.idle;
|
||||||
|
@ -146,13 +146,11 @@ class ClampingScrollSimulation extends Simulation {
|
|||||||
required this.velocity,
|
required this.velocity,
|
||||||
this.friction = 0.015,
|
this.friction = 0.015,
|
||||||
super.tolerance,
|
super.tolerance,
|
||||||
}) {
|
}) : assert(_flingVelocityPenetration(0.0) == _initialVelocityPenetration) {
|
||||||
_duration = _splineFlingDuration(velocity);
|
_duration = _flingDuration(velocity);
|
||||||
_distance = _splineFlingDistance(velocity);
|
_distance = (velocity * _duration / _initialVelocityPenetration).abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
final double _inflexion = 0.35;
|
|
||||||
|
|
||||||
/// The position of the particle at the beginning of the simulation.
|
/// The position of the particle at the beginning of the simulation.
|
||||||
final double position;
|
final double position;
|
||||||
|
|
||||||
@ -165,7 +163,7 @@ class ClampingScrollSimulation extends Simulation {
|
|||||||
/// The more friction the particle experiences, the sooner it stops.
|
/// The more friction the particle experiences, the sooner it stops.
|
||||||
final double friction;
|
final double friction;
|
||||||
|
|
||||||
late int _duration;
|
late double _duration;
|
||||||
late double _distance;
|
late double _distance;
|
||||||
|
|
||||||
// See DECELERATION_RATE.
|
// See DECELERATION_RATE.
|
||||||
@ -173,186 +171,59 @@ class ClampingScrollSimulation extends Simulation {
|
|||||||
|
|
||||||
// See computeDeceleration().
|
// See computeDeceleration().
|
||||||
static double _decelerationForFriction(double friction) {
|
static double _decelerationForFriction(double friction) {
|
||||||
return 9.80665 *
|
return friction * 61774.04968;
|
||||||
39.37 *
|
|
||||||
friction *
|
|
||||||
1.0 * // Flutter operates on logical pixels so the DPI should be 1.0.
|
|
||||||
160.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See getSplineDeceleration().
|
// See getSplineFlingDuration(). Returns a value in seconds.
|
||||||
double _splineDeceleration(double velocity) {
|
double _flingDuration(double velocity) {
|
||||||
return math.log(_inflexion *
|
// See mPhysicalCoeff
|
||||||
velocity.abs() /
|
final double scaledFriction = friction * _decelerationForFriction(0.84);
|
||||||
(friction * _decelerationForFriction(0.84)));
|
|
||||||
|
// See getSplineDeceleration().
|
||||||
|
final double deceleration = math.log(0.35 * velocity.abs() / scaledFriction);
|
||||||
|
|
||||||
|
return math.exp(deceleration / (_kDecelerationRate - 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// See getSplineFlingDuration().
|
// Based on a cubic curve fit to the Scroller.computeScrollOffset() values
|
||||||
int _splineFlingDuration(double velocity) {
|
// produced for an initial velocity of 4000. The value of Scroller.getDuration()
|
||||||
final double deceleration = _splineDeceleration(velocity);
|
// and Scroller.getFinalY() were 686ms and 961 pixels respectively.
|
||||||
return (1000 * math.exp(deceleration / (_kDecelerationRate - 1.0))).round();
|
//
|
||||||
|
// Algebra courtesy of Wolfram Alpha.
|
||||||
|
//
|
||||||
|
// f(x) = scrollOffset, x is time in milliseconds
|
||||||
|
// f(x) = 3.60882×10^-6 x^3 - 0.00668009 x^2 + 4.29427 x - 3.15307
|
||||||
|
// f(x) = 3.60882×10^-6 x^3 - 0.00668009 x^2 + 4.29427 x, so f(0) is 0
|
||||||
|
// f(686ms) = 961 pixels
|
||||||
|
// Scale to f(0 <= t <= 1.0), x = t * 686
|
||||||
|
// f(t) = 1165.03 t^3 - 3143.62 t^2 + 2945.87 t
|
||||||
|
// Scale f(t) so that 0.0 <= f(t) <= 1.0
|
||||||
|
// f(t) = (1165.03 t^3 - 3143.62 t^2 + 2945.87 t) / 961.0
|
||||||
|
// = 1.2 t^3 - 3.27 t^2 + 3.065 t
|
||||||
|
static const double _initialVelocityPenetration = 3.065;
|
||||||
|
static double _flingDistancePenetration(double t) {
|
||||||
|
return (1.2 * t * t * t) - (3.27 * t * t) + (_initialVelocityPenetration * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// See getSplineFlingDistance().
|
// The derivative of the _flingDistancePenetration() function.
|
||||||
double _splineFlingDistance(double velocity) {
|
static double _flingVelocityPenetration(double t) {
|
||||||
final double l = _splineDeceleration(velocity);
|
return (3.6 * t * t) - (6.54 * t) + _initialVelocityPenetration;
|
||||||
final double decelMinusOne = _kDecelerationRate - 1.0;
|
|
||||||
return friction *
|
|
||||||
_decelerationForFriction(0.84) *
|
|
||||||
math.exp(_kDecelerationRate / decelMinusOne * l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double x(double time) {
|
double x(double time) {
|
||||||
if (time == 0) {
|
final double t = clampDouble(time / _duration, 0.0, 1.0);
|
||||||
return position;
|
return position + _distance * _flingDistancePenetration(t) * velocity.sign;
|
||||||
}
|
|
||||||
final _NBSample sample = _NBSample(time, _duration);
|
|
||||||
return position + (sample.distanceCoef * _distance) * velocity.sign;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double dx(double time) {
|
double dx(double time) {
|
||||||
if (time == 0) {
|
final double t = clampDouble(time / _duration, 0.0, 1.0);
|
||||||
return velocity;
|
return _distance * _flingVelocityPenetration(t) * velocity.sign / _duration;
|
||||||
}
|
|
||||||
final _NBSample sample = _NBSample(time, _duration);
|
|
||||||
return sample.velocityCoef * _distance / _duration * velocity.sign * 1000.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isDone(double time) {
|
bool isDone(double time) {
|
||||||
return time * 1000.0 >= _duration;
|
return time >= _duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NBSample {
|
|
||||||
_NBSample(double time, int duration) {
|
|
||||||
// See computeScrollOffset().
|
|
||||||
final double t = time * 1000.0 / duration;
|
|
||||||
final int index = (_nbSamples * t).clamp(0, _nbSamples).round(); // ignore_clamp_double_lint
|
|
||||||
_distanceCoef = 1.0;
|
|
||||||
_velocityCoef = 0.0;
|
|
||||||
if (index < _nbSamples) {
|
|
||||||
final double tInf = index / _nbSamples;
|
|
||||||
final double tSup = (index + 1) / _nbSamples;
|
|
||||||
final double dInf = _splinePosition[index];
|
|
||||||
final double dSup = _splinePosition[index + 1];
|
|
||||||
_velocityCoef = (dSup - dInf) / (tSup - tInf);
|
|
||||||
_distanceCoef = dInf + (t - tInf) * _velocityCoef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
late double _velocityCoef;
|
|
||||||
double get velocityCoef => _velocityCoef;
|
|
||||||
|
|
||||||
late double _distanceCoef;
|
|
||||||
double get distanceCoef => _distanceCoef;
|
|
||||||
|
|
||||||
static const int _nbSamples = 100;
|
|
||||||
|
|
||||||
// Generated from dev/tools/generate_android_spline_data.dart.
|
|
||||||
static const List<double> _splinePosition = <double>[
|
|
||||||
0.000022888183591973643,
|
|
||||||
0.028561000304762274,
|
|
||||||
0.05705195792956655,
|
|
||||||
0.08538917797618413,
|
|
||||||
0.11349556286812107,
|
|
||||||
0.14129881694635613,
|
|
||||||
0.16877157254923383,
|
|
||||||
0.19581093511175632,
|
|
||||||
0.22239649722992452,
|
|
||||||
0.24843841866631658,
|
|
||||||
0.2740024733220569,
|
|
||||||
0.298967680744136,
|
|
||||||
0.32333234658228116,
|
|
||||||
0.34709556909569184,
|
|
||||||
0.3702249257894571,
|
|
||||||
0.39272483400399893,
|
|
||||||
0.41456988647721615,
|
|
||||||
0.43582889025419114,
|
|
||||||
0.4564192786416,
|
|
||||||
0.476410299013587,
|
|
||||||
0.4957560715637827,
|
|
||||||
0.5145493169954743,
|
|
||||||
0.5327205670880077,
|
|
||||||
0.5502846891191615,
|
|
||||||
0.5673274324802855,
|
|
||||||
0.583810881323224,
|
|
||||||
0.5997478744397482,
|
|
||||||
0.615194045299478,
|
|
||||||
0.6301165005270208,
|
|
||||||
0.6445484042257972,
|
|
||||||
0.6585198219185201,
|
|
||||||
0.6720397744233084,
|
|
||||||
0.6850997688076114,
|
|
||||||
0.6977281404741683,
|
|
||||||
0.7099506591298411,
|
|
||||||
0.7217749311525871,
|
|
||||||
0.7331784038850426,
|
|
||||||
0.7442308394229518,
|
|
||||||
0.7549087205105974,
|
|
||||||
0.7652471277371271,
|
|
||||||
0.7752251637549381,
|
|
||||||
0.7848768260203478,
|
|
||||||
0.7942056937103814,
|
|
||||||
0.8032299679689082,
|
|
||||||
0.8119428702388629,
|
|
||||||
0.8203713516576219,
|
|
||||||
0.8285187880808974,
|
|
||||||
0.8363794492831295,
|
|
||||||
0.8439768562813565,
|
|
||||||
0.851322799855549,
|
|
||||||
0.8584111051351724,
|
|
||||||
0.8652534074722162,
|
|
||||||
0.8718525580962131,
|
|
||||||
0.8782333271742155,
|
|
||||||
0.8843892099362031,
|
|
||||||
0.8903155590440985,
|
|
||||||
0.8960465359221951,
|
|
||||||
0.9015574505919048,
|
|
||||||
0.9068736766459904,
|
|
||||||
0.9119951682409297,
|
|
||||||
0.9169321898723632,
|
|
||||||
0.9216747065581234,
|
|
||||||
0.9262420604674766,
|
|
||||||
0.9306331858366086,
|
|
||||||
0.9348476990715433,
|
|
||||||
0.9389007110754832,
|
|
||||||
0.9427903495057521,
|
|
||||||
0.9465220679845756,
|
|
||||||
0.9500943036519721,
|
|
||||||
0.9535176728088761,
|
|
||||||
0.9567898524767604,
|
|
||||||
0.959924306623116,
|
|
||||||
0.9629127700159108,
|
|
||||||
0.9657622101750765,
|
|
||||||
0.9684818726275105,
|
|
||||||
0.9710676079044347,
|
|
||||||
0.9735231939498,
|
|
||||||
0.9758514437576309,
|
|
||||||
0.9780599066560445,
|
|
||||||
0.9801485715370128,
|
|
||||||
0.9821149805689633,
|
|
||||||
0.9839677526782791,
|
|
||||||
0.9857085499421516,
|
|
||||||
0.9873347811966005,
|
|
||||||
0.9888547171706613,
|
|
||||||
0.9902689443512227,
|
|
||||||
0.9915771042095881,
|
|
||||||
0.9927840651641069,
|
|
||||||
0.9938913963715834,
|
|
||||||
0.9948987305580712,
|
|
||||||
0.9958114963810524,
|
|
||||||
0.9966274782266875,
|
|
||||||
0.997352148697352,
|
|
||||||
0.9979848677523623,
|
|
||||||
0.9985285021374979,
|
|
||||||
0.9989844084453229,
|
|
||||||
0.9993537595844986,
|
|
||||||
0.999638729860106,
|
|
||||||
0.9998403888004533,
|
|
||||||
0.9999602810470701,
|
|
||||||
1.0
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
@ -129,64 +129,6 @@ void main() {
|
|||||||
expect(find.text('Page 9'), findsOneWidget);
|
expect(find.text('Page 9'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
List<Widget> childrenSizeIncrease(int n) {
|
|
||||||
return List<Widget>.generate(n, (int i) {
|
|
||||||
return SizedBox(height: 40.0 + i * 3, child: Text('$i'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
testWidgets('Check for duplicate pixels with ClampingScrollPhysics', (WidgetTester tester) async {
|
|
||||||
final List<double> scrollSimulationXList = <double>[];
|
|
||||||
final TestScrollPhysics testScrollPhysics = TestScrollPhysics(
|
|
||||||
scrollSimulationXList,
|
|
||||||
parent: const ClampingScrollPhysics(),
|
|
||||||
);
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: ListView(
|
|
||||||
physics: testScrollPhysics,
|
|
||||||
children: childrenSizeIncrease(100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await tester.fling(find.byType(ListView), const Offset(0.0, -4000.0), 4000.0);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
final Set<double> checkSet = <double>{};
|
|
||||||
checkSet.addAll(scrollSimulationXList);
|
|
||||||
/// checkSet.length + 1 is because:
|
|
||||||
/// simulation.x(0.0) will be called in _startSimulation.
|
|
||||||
/// The first frame of the animation will also call simulation.x(0.0).
|
|
||||||
/// It can be tolerated that it has at most one duplicate value.
|
|
||||||
final bool hasOnlyOneDuplicate = scrollSimulationXList.length == checkSet.length + 1;
|
|
||||||
expect(true, hasOnlyOneDuplicate); // and ends up at the end
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Check for duplicate pixels with BouncingScrollPhysics', (WidgetTester tester) async {
|
|
||||||
final List<double> scrollSimulationXList = <double>[];
|
|
||||||
final TestScrollPhysics testScrollPhysics = TestScrollPhysics(
|
|
||||||
scrollSimulationXList,
|
|
||||||
parent: const BouncingScrollPhysics(),
|
|
||||||
);
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: ListView(
|
|
||||||
physics: testScrollPhysics,
|
|
||||||
children: childrenSizeIncrease(100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await tester.fling(find.byType(ListView), const Offset(0.0, -4000.0), 4000.0);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
final Set<double> checkSet = <double>{};
|
|
||||||
checkSet.addAll(scrollSimulationXList);
|
|
||||||
/// checkSet.length + 1 is because:
|
|
||||||
/// simulation.x(0.0) will be call in _startSimulation.
|
|
||||||
/// The first frame of the animation will also call simulation.x(0.0).
|
|
||||||
/// It can be tolerated that it has at most one duplicate value.
|
|
||||||
final bool noDuplicate = scrollSimulationXList.length == checkSet.length + 1;
|
|
||||||
expect(true, noDuplicate); // and ends up at the end
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Pointer is not ignored during trackpad scrolling.', (WidgetTester tester) async {
|
testWidgets('Pointer is not ignored during trackpad scrolling.', (WidgetTester tester) async {
|
||||||
final ScrollController controller = ScrollController();
|
final ScrollController controller = ScrollController();
|
||||||
int? lastTapped;
|
int? lastTapped;
|
||||||
@ -268,62 +210,6 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestScrollPhysics extends ScrollPhysics {
|
|
||||||
const TestScrollPhysics(this.scrollSimulationXList, { super.parent });
|
|
||||||
|
|
||||||
final List<double> scrollSimulationXList;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Simulation? createBallisticSimulation(
|
|
||||||
ScrollMetrics position,
|
|
||||||
double velocity,
|
|
||||||
) {
|
|
||||||
final Simulation? scrollSimulation = super.createBallisticSimulation(
|
|
||||||
position,
|
|
||||||
velocity,
|
|
||||||
);
|
|
||||||
if (scrollSimulation != null && scrollSimulationXList != null) {
|
|
||||||
return TestScrollScrollSimulation(
|
|
||||||
scrollSimulation,
|
|
||||||
scrollSimulationXList,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return scrollSimulation;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TestScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
|
||||||
return TestScrollPhysics(
|
|
||||||
scrollSimulationXList,
|
|
||||||
parent: buildParent(ancestor),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestScrollScrollSimulation extends Simulation {
|
|
||||||
TestScrollScrollSimulation(this.innerScrollSimulation,
|
|
||||||
this.scrollSimulationXList,);
|
|
||||||
|
|
||||||
final Simulation innerScrollSimulation;
|
|
||||||
|
|
||||||
final List<double> scrollSimulationXList;
|
|
||||||
|
|
||||||
@override
|
|
||||||
double dx(double time) => innerScrollSimulation.dx(time);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool isDone(double time) => innerScrollSimulation.isDone(time);
|
|
||||||
|
|
||||||
@override
|
|
||||||
double x(double time) {
|
|
||||||
final double simulationX = innerScrollSimulation.x(time);
|
|
||||||
if (scrollSimulationXList != null) {
|
|
||||||
scrollSimulationXList.add(simulationX);
|
|
||||||
}
|
|
||||||
return simulationX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PageView62209 extends StatefulWidget {
|
class PageView62209 extends StatefulWidget {
|
||||||
const PageView62209({super.key});
|
const PageView62209({super.key});
|
||||||
|
|
||||||
|
@ -23,21 +23,4 @@ void main() {
|
|||||||
checkInitialConditions(75.0, 614.2093);
|
checkInitialConditions(75.0, 614.2093);
|
||||||
checkInitialConditions(5469.0, 182.114534);
|
checkInitialConditions(5469.0, 182.114534);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ClampingScrollSimulation velocity eventually reaches zero', () {
|
|
||||||
void checkFinalConditions(double position, double velocity) {
|
|
||||||
final ClampingScrollSimulation simulation = ClampingScrollSimulation(position: position, velocity: velocity);
|
|
||||||
expect(simulation.dx(10.0), equals(0.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
checkFinalConditions(51.0, 2000.0);
|
|
||||||
checkFinalConditions(584.0, 2617.294734);
|
|
||||||
checkFinalConditions(345.0, 1982.785934);
|
|
||||||
checkFinalConditions(0.0, 1831.366634);
|
|
||||||
checkFinalConditions(-156.2, 1541.57665);
|
|
||||||
checkFinalConditions(4.0, 1139.250439);
|
|
||||||
checkFinalConditions(4534.0, 1073.553798);
|
|
||||||
checkFinalConditions(75.0, 614.2093);
|
|
||||||
checkFinalConditions(5469.0, 182.114534);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ void main() {
|
|||||||
// Before changing these values, ensure the fling results in a distance that
|
// Before changing these values, ensure the fling results in a distance that
|
||||||
// makes sense. See issue for more context.
|
// makes sense. See issue for more context.
|
||||||
expect(androidResult, greaterThan(394.0));
|
expect(androidResult, greaterThan(394.0));
|
||||||
expect(androidResult, lessThan(408.2));
|
expect(androidResult, lessThan(395.0));
|
||||||
|
|
||||||
await pumpTest(tester, TargetPlatform.linux);
|
await pumpTest(tester, TargetPlatform.linux);
|
||||||
await tester.fling(find.byType(ListView), const Offset(0.0, -dragOffset), 1000.0);
|
await tester.fling(find.byType(ListView), const Offset(0.0, -dragOffset), 1000.0);
|
||||||
@ -152,6 +152,6 @@ void main() {
|
|||||||
expect(log, equals(<String>['tap 21']));
|
expect(log, equals(<String>['tap 21']));
|
||||||
await tester.tap(find.byType(Scrollable));
|
await tester.tap(find.byType(Scrollable));
|
||||||
await tester.pump(const Duration(milliseconds: 50));
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
expect(log, equals(<String>['tap 21', 'tap 49']));
|
expect(log, equals(<String>['tap 21', 'tap 48']));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ void main() {
|
|||||||
|
|
||||||
expect(semantics, includesNodeWith(
|
expect(semantics, includesNodeWith(
|
||||||
scrollExtentMin: 0.0,
|
scrollExtentMin: 0.0,
|
||||||
scrollPosition: 394.3,
|
scrollPosition: 380.2,
|
||||||
scrollExtentMax: 520.0,
|
scrollExtentMax: 520.0,
|
||||||
actions: <SemanticsAction>[
|
actions: <SemanticsAction>[
|
||||||
SemanticsAction.scrollUp,
|
SemanticsAction.scrollUp,
|
||||||
@ -280,7 +280,7 @@ void main() {
|
|||||||
|
|
||||||
expect(semantics, includesNodeWith(
|
expect(semantics, includesNodeWith(
|
||||||
scrollExtentMin: 0.0,
|
scrollExtentMin: 0.0,
|
||||||
scrollPosition: 394.3,
|
scrollPosition: 380.2,
|
||||||
scrollExtentMax: double.infinity,
|
scrollExtentMax: double.infinity,
|
||||||
actions: <SemanticsAction>[
|
actions: <SemanticsAction>[
|
||||||
SemanticsAction.scrollUp,
|
SemanticsAction.scrollUp,
|
||||||
@ -292,7 +292,7 @@ void main() {
|
|||||||
|
|
||||||
expect(semantics, includesNodeWith(
|
expect(semantics, includesNodeWith(
|
||||||
scrollExtentMin: 0.0,
|
scrollExtentMin: 0.0,
|
||||||
scrollPosition: 788.6,
|
scrollPosition: 760.4,
|
||||||
scrollExtentMax: double.infinity,
|
scrollExtentMax: double.infinity,
|
||||||
actions: <SemanticsAction>[
|
actions: <SemanticsAction>[
|
||||||
SemanticsAction.scrollUp,
|
SemanticsAction.scrollUp,
|
||||||
|
@ -946,8 +946,8 @@ void main() {
|
|||||||
expect(find.byKey(const ValueKey<String>('Box 0')), findsNothing);
|
expect(find.byKey(const ValueKey<String>('Box 0')), findsNothing);
|
||||||
expect(find.byKey(const ValueKey<String>('Box 52')), findsOneWidget);
|
expect(find.byKey(const ValueKey<String>('Box 52')), findsOneWidget);
|
||||||
|
|
||||||
expect(expensiveWidgets, 40);
|
expect(expensiveWidgets, 38);
|
||||||
expect(cheapWidgets, 21);
|
expect(cheapWidgets, 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can recommendDeferredLoadingForContext - override heuristic', (WidgetTester tester) async {
|
testWidgets('Can recommendDeferredLoadingForContext - override heuristic', (WidgetTester tester) async {
|
||||||
@ -989,9 +989,9 @@ void main() {
|
|||||||
expect(find.byKey(const ValueKey<String>('Box 0')), findsNothing);
|
expect(find.byKey(const ValueKey<String>('Box 0')), findsNothing);
|
||||||
expect(find.byKey(const ValueKey<String>('Cheap box 52')), findsOneWidget);
|
expect(find.byKey(const ValueKey<String>('Cheap box 52')), findsOneWidget);
|
||||||
|
|
||||||
expect(expensiveWidgets, 17);
|
expect(expensiveWidgets, 18);
|
||||||
expect(cheapWidgets, 44);
|
expect(cheapWidgets, 40);
|
||||||
expect(physics.count, 44 + 17);
|
expect(physics.count, 40 + 18);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can recommendDeferredLoadingForContext - override heuristic and always return true', (WidgetTester tester) async {
|
testWidgets('Can recommendDeferredLoadingForContext - override heuristic and always return true', (WidgetTester tester) async {
|
||||||
@ -1032,7 +1032,7 @@ void main() {
|
|||||||
expect(find.byKey(const ValueKey<String>('Cheap box 52')), findsOneWidget);
|
expect(find.byKey(const ValueKey<String>('Cheap box 52')), findsOneWidget);
|
||||||
|
|
||||||
expect(expensiveWidgets, 0);
|
expect(expensiveWidgets, 0);
|
||||||
expect(cheapWidgets, 61);
|
expect(cheapWidgets, 58);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ensureVisible does not move PageViews', (WidgetTester tester) async {
|
testWidgets('ensureVisible does not move PageViews', (WidgetTester tester) async {
|
||||||
@ -1439,9 +1439,9 @@ void main() {
|
|||||||
await tester.sendEventToBinding(testPointer.hover(tester.getCenter(find.byType(Scrollable))));
|
await tester.sendEventToBinding(testPointer.hover(tester.getCenter(find.byType(Scrollable))));
|
||||||
await tester.sendEventToBinding(testPointer.scrollInertiaCancel()); // Cancel partway through.
|
await tester.sendEventToBinding(testPointer.scrollInertiaCancel()); // Cancel partway through.
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(getScrollOffset(tester), closeTo(342.5439, 0.0001));
|
expect(getScrollOffset(tester), closeTo(333.2944, 0.0001));
|
||||||
await tester.pump(const Duration(milliseconds: 4800));
|
await tester.pump(const Duration(milliseconds: 4800));
|
||||||
expect(getScrollOffset(tester), closeTo(342.5439, 0.0001));
|
expect(getScrollOffset(tester), closeTo(333.2944, 0.0001));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
|
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true));
|
||||||
|
|
||||||
await tester.fling(find.text('Tile 2'), const Offset(0, -600), 1950);
|
await tester.fling(find.text('Tile 2'), const Offset(0, -600), 2000);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expectedSemantics = TestSemantics.root(
|
expectedSemantics = TestSemantics.root(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user