mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Make CircularProgressIndicator's animation match native (#50412)
This commit is contained in:
parent
32547dcc7e
commit
e5bd2b3d55
|
@ -12,6 +12,7 @@ import 'theme.dart';
|
|||
|
||||
const double _kMinCircularProgressIndicatorSize = 36.0;
|
||||
const int _kIndeterminateLinearDuration = 1800;
|
||||
const int _kIndeterminateCircularDuration = 1333 * 2222;
|
||||
|
||||
/// A base class for material design progress indicators.
|
||||
///
|
||||
|
@ -331,12 +332,12 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
|
|||
this.value,
|
||||
this.headValue,
|
||||
this.tailValue,
|
||||
this.stepValue,
|
||||
this.offsetValue,
|
||||
this.rotationValue,
|
||||
this.strokeWidth,
|
||||
}) : arcStart = value != null
|
||||
? _startAngle
|
||||
: _startAngle + tailValue * 3 / 2 * math.pi + rotationValue * math.pi * 1.7 - stepValue * 0.8 * math.pi,
|
||||
: _startAngle + tailValue * 3 / 2 * math.pi + rotationValue * math.pi * 2.0 + offsetValue * 0.5 * math.pi,
|
||||
arcSweep = value != null
|
||||
? (value.clamp(0.0, 1.0) as double) * _sweep
|
||||
: math.max(headValue * 3 / 2 * math.pi - tailValue * 3 / 2 * math.pi, _epsilon);
|
||||
|
@ -346,7 +347,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
|
|||
final double value;
|
||||
final double headValue;
|
||||
final double tailValue;
|
||||
final int stepValue;
|
||||
final double offsetValue;
|
||||
final double rotationValue;
|
||||
final double strokeWidth;
|
||||
final double arcStart;
|
||||
|
@ -385,7 +386,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
|
|||
|| oldPainter.value != value
|
||||
|| oldPainter.headValue != headValue
|
||||
|| oldPainter.tailValue != tailValue
|
||||
|| oldPainter.stepValue != stepValue
|
||||
|| oldPainter.offsetValue != offsetValue
|
||||
|| oldPainter.rotationValue != rotationValue
|
||||
|| oldPainter.strokeWidth != strokeWidth;
|
||||
}
|
||||
|
@ -443,31 +444,30 @@ class CircularProgressIndicator extends ProgressIndicator {
|
|||
_CircularProgressIndicatorState createState() => _CircularProgressIndicatorState();
|
||||
}
|
||||
|
||||
// Tweens used by circular progress indicator
|
||||
final Animatable<double> _kStrokeHeadTween = CurveTween(
|
||||
curve: const Interval(0.0, 0.5, curve: Curves.fastOutSlowIn),
|
||||
).chain(CurveTween(
|
||||
curve: const SawTooth(5),
|
||||
));
|
||||
|
||||
final Animatable<double> _kStrokeTailTween = CurveTween(
|
||||
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
|
||||
).chain(CurveTween(
|
||||
curve: const SawTooth(5),
|
||||
));
|
||||
|
||||
final Animatable<int> _kStepTween = StepTween(begin: 0, end: 5);
|
||||
|
||||
final Animatable<double> _kRotationTween = CurveTween(curve: const SawTooth(5));
|
||||
|
||||
class _CircularProgressIndicatorState extends State<CircularProgressIndicator> with SingleTickerProviderStateMixin {
|
||||
static const int _pathCount = _kIndeterminateCircularDuration ~/ 1333;
|
||||
static const int _rotationCount = _kIndeterminateCircularDuration ~/ 2222;
|
||||
|
||||
static final Animatable<double> _strokeHeadTween = CurveTween(
|
||||
curve: const Interval(0.0, 0.5, curve: Curves.fastOutSlowIn),
|
||||
).chain(CurveTween(
|
||||
curve: const SawTooth(_pathCount),
|
||||
));
|
||||
static final Animatable<double> _strokeTailTween = CurveTween(
|
||||
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
|
||||
).chain(CurveTween(
|
||||
curve: const SawTooth(_pathCount),
|
||||
));
|
||||
static final Animatable<double> _offsetTween = CurveTween(curve: const SawTooth(_pathCount));
|
||||
static final Animatable<double> _rotationTween = CurveTween(curve: const SawTooth(_rotationCount));
|
||||
|
||||
AnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(seconds: 5),
|
||||
duration: const Duration(milliseconds: _kIndeterminateCircularDuration),
|
||||
vsync: this,
|
||||
);
|
||||
if (widget.value == null)
|
||||
|
@ -489,7 +489,7 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> w
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, int stepValue, double rotationValue) {
|
||||
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, double offsetValue, double rotationValue) {
|
||||
return widget._buildSemanticsWrapper(
|
||||
context: context,
|
||||
child: Container(
|
||||
|
@ -504,7 +504,7 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> w
|
|||
value: widget.value, // may be null
|
||||
headValue: headValue, // remaining arguments are ignored if widget.value is not null
|
||||
tailValue: tailValue,
|
||||
stepValue: stepValue,
|
||||
offsetValue: offsetValue,
|
||||
rotationValue: rotationValue,
|
||||
strokeWidth: widget.strokeWidth,
|
||||
),
|
||||
|
@ -519,10 +519,10 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> w
|
|||
builder: (BuildContext context, Widget child) {
|
||||
return _buildIndicator(
|
||||
context,
|
||||
_kStrokeHeadTween.evaluate(_controller),
|
||||
_kStrokeTailTween.evaluate(_controller),
|
||||
_kStepTween.evaluate(_controller),
|
||||
_kRotationTween.evaluate(_controller),
|
||||
_strokeHeadTween.evaluate(_controller),
|
||||
_strokeTailTween.evaluate(_controller),
|
||||
_offsetTween.evaluate(_controller),
|
||||
_rotationTween.evaluate(_controller),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -542,7 +542,7 @@ class _RefreshProgressIndicatorPainter extends _CircularProgressIndicatorPainter
|
|||
double value,
|
||||
double headValue,
|
||||
double tailValue,
|
||||
int stepValue,
|
||||
double offsetValue,
|
||||
double rotationValue,
|
||||
double strokeWidth,
|
||||
this.arrowheadScale,
|
||||
|
@ -551,7 +551,7 @@ class _RefreshProgressIndicatorPainter extends _CircularProgressIndicatorPainter
|
|||
value: value,
|
||||
headValue: headValue,
|
||||
tailValue: tailValue,
|
||||
stepValue: stepValue,
|
||||
offsetValue: offsetValue,
|
||||
rotationValue: rotationValue,
|
||||
strokeWidth: strokeWidth,
|
||||
);
|
||||
|
@ -645,14 +645,14 @@ class _RefreshProgressIndicatorState extends _CircularProgressIndicatorState {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.value != null)
|
||||
_controller.value = widget.value / 10.0;
|
||||
_controller.value = widget.value * (1333 / 2 / _kIndeterminateCircularDuration);
|
||||
else if (!_controller.isAnimating)
|
||||
_controller.repeat();
|
||||
return _buildAnimation();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, int stepValue, double rotationValue) {
|
||||
Widget _buildIndicator(BuildContext context, double headValue, double tailValue, double offsetValue, double rotationValue) {
|
||||
final double arrowheadScale = widget.value == null ? 0.0 : ((widget.value * 2.0).clamp(0.0, 1.0) as double);
|
||||
return widget._buildSemanticsWrapper(
|
||||
context: context,
|
||||
|
@ -672,7 +672,7 @@ class _RefreshProgressIndicatorState extends _CircularProgressIndicatorState {
|
|||
value: null, // Draw the indeterminate progress indicator.
|
||||
headValue: headValue,
|
||||
tailValue: tailValue,
|
||||
stepValue: stepValue,
|
||||
offsetValue: offsetValue,
|
||||
rotationValue: rotationValue,
|
||||
strokeWidth: widget.strokeWidth,
|
||||
arrowheadScale: arrowheadScale,
|
||||
|
|
|
@ -530,4 +530,28 @@ void main() {
|
|||
|
||||
handle.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Indeterminate CircularProgressIndicator uses expected animation', (WidgetTester tester) async {
|
||||
final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));
|
||||
|
||||
await tester.pumpFrames(animationSheet.record(
|
||||
const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
), const Duration(seconds: 2));
|
||||
|
||||
tester.binding.setSurfaceSize(animationSheet.sheetSize());
|
||||
|
||||
final Widget display = await animationSheet.display();
|
||||
await tester.pumpWidget(display);
|
||||
|
||||
await expectLater(
|
||||
find.byWidget(display),
|
||||
matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
|
||||
);
|
||||
}, skip: isBrowser);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue