Skip to content

Commit 4ec7558

Browse files
authored
Revert clamping scroll simulation changes (#89885)
1 parent a489fff commit 4ec7558

8 files changed

Lines changed: 58 additions & 257 deletions

‎dev/tools/generate_android_spline_data.dart‎

Lines changed: 0 additions & 61 deletions
This file was deleted.

‎packages/flutter/lib/src/widgets/scroll_simulation.dart‎

Lines changed: 41 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ class BouncingScrollSimulation extends Simulation {
129129
}
130130
}
131131

132-
const double _inflexion = 0.35;
133-
134132
/// An implementation of scroll physics that matches Android.
135133
///
136134
/// See also:
@@ -149,9 +147,10 @@ class ClampingScrollSimulation extends Simulation {
149147
required this.velocity,
150148
this.friction = 0.015,
151149
Tolerance tolerance = Tolerance.defaultTolerance,
152-
}) : super(tolerance: tolerance) {
153-
_duration = _splineFlingDuration(velocity);
154-
_distance = _splineFlingDistance(velocity);
150+
}) : assert(_flingVelocityPenetration(0.0) == _initialVelocityPenetration),
151+
super(tolerance: tolerance) {
152+
_duration = _flingDuration(velocity);
153+
_distance = (velocity * _duration / _initialVelocityPenetration).abs();
155154
}
156155

157156
/// The position of the particle at the beginning of the simulation.
@@ -166,192 +165,67 @@ class ClampingScrollSimulation extends Simulation {
166165
/// The more friction the particle experiences, the sooner it stops.
167166
final double friction;
168167

169-
late int _duration;
168+
late double _duration;
170169
late double _distance;
171170

172171
// See DECELERATION_RATE.
173172
static final double _kDecelerationRate = math.log(0.78) / math.log(0.9);
174173

175174
// See computeDeceleration().
176175
static double _decelerationForFriction(double friction) {
177-
return 9.80665 *
178-
39.37 *
179-
friction *
180-
1.0 * // Flutter operates on logical pixels so the DPI should be 1.0.
181-
160.0;
176+
return friction * 61774.04968;
182177
}
183178

184-
// See getSplineDeceleration().
185-
double _splineDeceleration(double velocity) {
186-
return math.log(_inflexion * velocity.abs() / (friction * _decelerationForFriction(0.84)));
179+
// See getSplineFlingDuration(). Returns a value in seconds.
180+
double _flingDuration(double velocity) {
181+
// See mPhysicalCoeff
182+
final double scaledFriction = friction * _decelerationForFriction(0.84);
183+
184+
// See getSplineDeceleration().
185+
final double deceleration = math.log(0.35 * velocity.abs() / scaledFriction);
186+
187+
return math.exp(deceleration / (_kDecelerationRate - 1.0));
187188
}
188189

189-
// See getSplineFlingDuration().
190-
int _splineFlingDuration(double velocity) {
191-
final double deceleration = _splineDeceleration(velocity);
192-
return (1000 * math.exp(deceleration / (_kDecelerationRate - 1.0))).round();
190+
// Based on a cubic curve fit to the Scroller.computeScrollOffset() values
191+
// produced for an initial velocity of 4000. The value of Scroller.getDuration()
192+
// and Scroller.getFinalY() were 686ms and 961 pixels respectively.
193+
//
194+
// Algebra courtesy of Wolfram Alpha.
195+
//
196+
// f(x) = scrollOffset, x is time in milliseconds
197+
// f(x) = 3.60882×10^-6 x^3 - 0.00668009 x^2 + 4.29427 x - 3.15307
198+
// f(x) = 3.60882×10^-6 x^3 - 0.00668009 x^2 + 4.29427 x, so f(0) is 0
199+
// f(686ms) = 961 pixels
200+
// Scale to f(0 <= t <= 1.0), x = t * 686
201+
// f(t) = 1165.03 t^3 - 3143.62 t^2 + 2945.87 t
202+
// Scale f(t) so that 0.0 <= f(t) <= 1.0
203+
// f(t) = (1165.03 t^3 - 3143.62 t^2 + 2945.87 t) / 961.0
204+
// = 1.2 t^3 - 3.27 t^2 + 3.065 t
205+
static const double _initialVelocityPenetration = 3.065;
206+
static double _flingDistancePenetration(double t) {
207+
return (1.2 * t * t * t) - (3.27 * t * t) + (_initialVelocityPenetration * t);
193208
}
194209

195-
// See getSplineFlingDistance().
196-
double _splineFlingDistance(double velocity) {
197-
final double l = _splineDeceleration(velocity);
198-
final double decelMinusOne = _kDecelerationRate - 1.0;
199-
return friction *
200-
_decelerationForFriction(0.84) *
201-
math.exp(_kDecelerationRate / decelMinusOne * l);
210+
// The derivative of the _flingDistancePenetration() function.
211+
static double _flingVelocityPenetration(double t) {
212+
return (3.6 * t * t) - (6.54 * t) + _initialVelocityPenetration;
202213
}
203214

204215
@override
205216
double x(double time) {
206-
if (time == 0) {
207-
return position;
208-
}
209-
final _NBSample sample = _NBSample(time, _duration);
210-
return position + (sample.distanceCoef * _distance) * velocity.sign;
217+
final double t = (time / _duration).clamp(0.0, 1.0);
218+
return position + _distance * _flingDistancePenetration(t) * velocity.sign;
211219
}
212220

213221
@override
214222
double dx(double time) {
215-
if (time == 0) {
216-
return velocity;
217-
}
218-
final _NBSample sample = _NBSample(time, _duration);
219-
return sample.velocityCoef * _distance / _duration * velocity.sign * 1000.0;
223+
final double t = (time / _duration).clamp(0.0, 1.0);
224+
return _distance * _flingVelocityPenetration(t) * velocity.sign / _duration;
220225
}
221226

222227
@override
223228
bool isDone(double time) {
224-
return time * 1000.0 >= _duration;
225-
}
226-
}
227-
228-
class _NBSample {
229-
_NBSample(double time, int duration) {
230-
// See computeScrollOffset().
231-
final double t = time * 1000.0 / duration;
232-
final int index = (_nbSamples * t).clamp(0, _nbSamples).round();
233-
_distanceCoef = 1.0;
234-
_velocityCoef = 0.0;
235-
if (index < _nbSamples) {
236-
final double tInf = index / _nbSamples;
237-
final double tSup = (index + 1) / _nbSamples;
238-
final double dInf = _splinePosition[index];
239-
final double dSup = _splinePosition[index + 1];
240-
_velocityCoef = (dSup - dInf) / (tSup - tInf);
241-
_distanceCoef = dInf + (t - tInf) * _velocityCoef;
242-
}
229+
return time >= _duration;
243230
}
244-
245-
late double _velocityCoef;
246-
double get velocityCoef => _velocityCoef;
247-
248-
late double _distanceCoef;
249-
double get distanceCoef => _distanceCoef;
250-
251-
static const int _nbSamples = 100;
252-
253-
// Generated from dev/tools/generate_android_spline_data.dart.
254-
static final List<double> _splinePosition = <double>[
255-
0.000022888183591973643,
256-
0.028561000304762274,
257-
0.05705195792956655,
258-
0.08538917797618413,
259-
0.11349556286812107,
260-
0.14129881694635613,
261-
0.16877157254923383,
262-
0.19581093511175632,
263-
0.22239649722992452,
264-
0.24843841866631658,
265-
0.2740024733220569,
266-
0.298967680744136,
267-
0.32333234658228116,
268-
0.34709556909569184,
269-
0.3702249257894571,
270-
0.39272483400399893,
271-
0.41456988647721615,
272-
0.43582889025419114,
273-
0.4564192786416,
274-
0.476410299013587,
275-
0.4957560715637827,
276-
0.5145493169954743,
277-
0.5327205670880077,
278-
0.5502846891191615,
279-
0.5673274324802855,
280-
0.583810881323224,
281-
0.5997478744397482,
282-
0.615194045299478,
283-
0.6301165005270208,
284-
0.6445484042257972,
285-
0.6585198219185201,
286-
0.6720397744233084,
287-
0.6850997688076114,
288-
0.6977281404741683,
289-
0.7099506591298411,
290-
0.7217749311525871,
291-
0.7331784038850426,
292-
0.7442308394229518,
293-
0.7549087205105974,
294-
0.7652471277371271,
295-
0.7752251637549381,
296-
0.7848768260203478,
297-
0.7942056937103814,
298-
0.8032299679689082,
299-
0.8119428702388629,
300-
0.8203713516576219,
301-
0.8285187880808974,
302-
0.8363794492831295,
303-
0.8439768562813565,
304-
0.851322799855549,
305-
0.8584111051351724,
306-
0.8652534074722162,
307-
0.8718525580962131,
308-
0.8782333271742155,
309-
0.8843892099362031,
310-
0.8903155590440985,
311-
0.8960465359221951,
312-
0.9015574505919048,
313-
0.9068736766459904,
314-
0.9119951682409297,
315-
0.9169321898723632,
316-
0.9216747065581234,
317-
0.9262420604674766,
318-
0.9306331858366086,
319-
0.9348476990715433,
320-
0.9389007110754832,
321-
0.9427903495057521,
322-
0.9465220679845756,
323-
0.9500943036519721,
324-
0.9535176728088761,
325-
0.9567898524767604,
326-
0.959924306623116,
327-
0.9629127700159108,
328-
0.9657622101750765,
329-
0.9684818726275105,
330-
0.9710676079044347,
331-
0.9735231939498,
332-
0.9758514437576309,
333-
0.9780599066560445,
334-
0.9801485715370128,
335-
0.9821149805689633,
336-
0.9839677526782791,
337-
0.9857085499421516,
338-
0.9873347811966005,
339-
0.9888547171706613,
340-
0.9902689443512227,
341-
0.9915771042095881,
342-
0.9927840651641069,
343-
0.9938913963715834,
344-
0.9948987305580712,
345-
0.9958114963810524,
346-
0.9966274782266875,
347-
0.997352148697352,
348-
0.9979848677523623,
349-
0.9985285021374979,
350-
0.9989844084453229,
351-
0.9993537595844986,
352-
0.999638729860106,
353-
0.9998403888004533,
354-
0.9999602810470701,
355-
1.0,
356-
];
357231
}

‎packages/flutter/test/widgets/list_wheel_scroll_view_test.dart‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1236,7 +1236,7 @@ void main() {
12361236
await tester.fling(
12371237
find.byType(ListWheelScrollView),
12381238
const Offset(0.0, -50.0),
1239-
100.0,
1239+
800.0,
12401240
);
12411241

12421242
// At this moment, the ballistics is started but 50px is still inside the

‎packages/flutter/test/widgets/scroll_simulation_test.dart‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,4 @@ void main() {
2323
checkInitialConditions(75.0, 614.2093);
2424
checkInitialConditions(5469.0, 182.114534);
2525
});
26-
27-
test('ClampingScrollSimulation velocity eventually reaches zero', () {
28-
void checkFinalConditions(double position, double velocity) {
29-
final ClampingScrollSimulation simulation = ClampingScrollSimulation(position: position, velocity: velocity);
30-
expect(simulation.dx(10.0), equals(0.0));
31-
}
32-
33-
checkFinalConditions(51.0, 2000.0);
34-
checkFinalConditions(584.0, 2617.294734);
35-
checkFinalConditions(345.0, 1982.785934);
36-
checkFinalConditions(0.0, 1831.366634);
37-
checkFinalConditions(-156.2, 1541.57665);
38-
checkFinalConditions(4.0, 1139.250439);
39-
checkFinalConditions(4534.0, 1073.553798);
40-
checkFinalConditions(75.0, 614.2093);
41-
checkFinalConditions(5469.0, 182.114534);
42-
});
4326
}

‎packages/flutter/test/widgets/scrollable_fling_test.dart‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ void main() {
4444
expect(getCurrentOffset(), dragOffset);
4545
await tester.pump(const Duration(seconds: 5));
4646
final double androidResult = getCurrentOffset();
47+
// Regression test for https://github.com/flutter/flutter/issues/83632
48+
// Before changing these values, ensure the fling results in a distance that
49+
// makes sense. See issue for more context.
50+
expect(androidResult, greaterThan(394.0));
51+
expect(androidResult, lessThan(395.0));
4752

4853
await pumpTest(tester, TargetPlatform.linux);
4954
await tester.fling(find.byType(ListView), const Offset(0.0, -dragOffset), 1000.0);
@@ -147,6 +152,6 @@ void main() {
147152
expect(log, equals(<String>['tap 21']));
148153
await tester.tap(find.byType(Scrollable));
149154
await tester.pump(const Duration(milliseconds: 50));
150-
expect(log, equals(<String>['tap 21', 'tap 49']));
155+
expect(log, equals(<String>['tap 21', 'tap 48']));
151156
});
152157
}

‎packages/flutter/test/widgets/scrollable_semantics_test.dart‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ void main() {
233233

234234
expect(semantics, includesNodeWith(
235235
scrollExtentMin: 0.0,
236-
scrollPosition: 394.3,
236+
scrollPosition: 380.2,
237237
scrollExtentMax: 520.0,
238238
actions: <SemanticsAction>[
239239
SemanticsAction.scrollUp,
@@ -282,7 +282,7 @@ void main() {
282282

283283
expect(semantics, includesNodeWith(
284284
scrollExtentMin: 0.0,
285-
scrollPosition: 394.3,
285+
scrollPosition: 380.2,
286286
scrollExtentMax: double.infinity,
287287
actions: <SemanticsAction>[
288288
SemanticsAction.scrollUp,
@@ -294,7 +294,7 @@ void main() {
294294

295295
expect(semantics, includesNodeWith(
296296
scrollExtentMin: 0.0,
297-
scrollPosition: 788.6,
297+
scrollPosition: 760.4,
298298
scrollExtentMax: double.infinity,
299299
actions: <SemanticsAction>[
300300
SemanticsAction.scrollUp,

0 commit comments

Comments
 (0)