mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Implement new activity indicator for iOS14 (#60179)
Updates the activity indicator style to iOS14, but places it behind a flag, to be deprecated when iOS 14 is released.
This commit is contained in:
parent
ce55e42f2b
commit
c047769487
|
@ -18,6 +18,15 @@ const Color _kActiveTickColor = CupertinoDynamicColor.withBrightness(
|
|||
darkColor: Color(0xFFEBEBF5),
|
||||
);
|
||||
|
||||
/// Define the iOS version style of [CupertinoActivityIndicator].
|
||||
enum CupertinoActivityIndicatorIOSVersionStyle {
|
||||
/// The style that is used in iOS13 and earlier (12 points).
|
||||
iOS13,
|
||||
|
||||
/// The style that was introduced in iOS14 (8 points).
|
||||
iOS14,
|
||||
}
|
||||
|
||||
/// An iOS-style activity indicator that spins clockwise.
|
||||
///
|
||||
/// {@youtube 560 315 https://www.youtube.com/watch?v=AENVH-ZqKDQ}
|
||||
|
@ -31,6 +40,7 @@ class CupertinoActivityIndicator extends StatefulWidget {
|
|||
Key key,
|
||||
this.animating = true,
|
||||
this.radius = _kDefaultIndicatorRadius,
|
||||
this.iOSVersionStyle = CupertinoActivityIndicatorIOSVersionStyle.iOS13,
|
||||
}) : assert(animating != null),
|
||||
assert(radius != null),
|
||||
assert(radius > 0.0),
|
||||
|
@ -47,6 +57,7 @@ class CupertinoActivityIndicator extends StatefulWidget {
|
|||
Key key,
|
||||
this.radius = _kDefaultIndicatorRadius,
|
||||
this.progress = 1.0,
|
||||
this.iOSVersionStyle = CupertinoActivityIndicatorIOSVersionStyle.iOS13,
|
||||
}) : assert(radius != null),
|
||||
assert(radius > 0.0),
|
||||
assert(progress != null),
|
||||
|
@ -73,12 +84,19 @@ class CupertinoActivityIndicator extends StatefulWidget {
|
|||
/// Defaults to 1.0. Must be between 0.0 and 1.0 inclusive, and cannot be null.
|
||||
final double progress;
|
||||
|
||||
/// The iOS version style of activity indicator.
|
||||
///
|
||||
/// Defaults to [CupertinoActivityIndicatorIOSVersionStyle.iOS13].
|
||||
// TODO(ctrysbita): Change default style to iOS14 after official release, https://github.com/flutter/flutter/issues/60047
|
||||
final CupertinoActivityIndicatorIOSVersionStyle iOSVersionStyle;
|
||||
|
||||
@override
|
||||
_CupertinoActivityIndicatorState createState() => _CupertinoActivityIndicatorState();
|
||||
_CupertinoActivityIndicatorState createState() =>
|
||||
_CupertinoActivityIndicatorState();
|
||||
}
|
||||
|
||||
|
||||
class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator> with SingleTickerProviderStateMixin {
|
||||
class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AnimationController _controller;
|
||||
|
||||
@override
|
||||
|
@ -89,9 +107,10 @@ class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
|
|||
vsync: this,
|
||||
);
|
||||
|
||||
if (widget.animating)
|
||||
if (widget.animating) {
|
||||
_controller.repeat();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoActivityIndicator oldWidget) {
|
||||
|
@ -118,9 +137,11 @@ class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
|
|||
child: CustomPaint(
|
||||
painter: _CupertinoActivityIndicatorPainter(
|
||||
position: _controller,
|
||||
activeColor: CupertinoDynamicColor.resolve(_kActiveTickColor, context),
|
||||
activeColor:
|
||||
CupertinoDynamicColor.resolve(_kActiveTickColor, context),
|
||||
radius: widget.radius,
|
||||
progress: widget.progress,
|
||||
iOSVersionStyle: widget.iOSVersionStyle,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -128,15 +149,42 @@ class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
|
|||
}
|
||||
|
||||
const double _kTwoPI = math.pi * 2.0;
|
||||
const int _kTickCount = 12;
|
||||
|
||||
// Alpha values extracted from the native component (for both dark and light mode) to
|
||||
// draw the spinning ticks. The list must have a length of _kTickCount. The order of
|
||||
// these values is designed to match the first frame of the iOS activity indicator which
|
||||
// has the most prominent tick at 9 o'clock.
|
||||
const List<int> _alphaValues = <int>[47, 47, 47, 47, 64, 81, 97, 114, 131, 147, 47, 47];
|
||||
/// Alpha values extracted from the native component (for both dark and light mode) to
|
||||
/// draw the spinning ticks.
|
||||
const Map<CupertinoActivityIndicatorIOSVersionStyle, List<int>>
|
||||
_kAlphaValuesMap = <CupertinoActivityIndicatorIOSVersionStyle, List<int>>{
|
||||
/// The order of these values is designed to match the first frame of the iOS activity indicator which
|
||||
/// has the most prominent tick at 9 o'clock.
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS13: <int>[
|
||||
47,
|
||||
47,
|
||||
47,
|
||||
47,
|
||||
64,
|
||||
81,
|
||||
97,
|
||||
114,
|
||||
131,
|
||||
147,
|
||||
47,
|
||||
47
|
||||
],
|
||||
|
||||
// The alpha value that is used to draw the partially revealed ticks.
|
||||
/// Alpha values for new style that introduced in iOS14.
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS14: <int>[
|
||||
47,
|
||||
47,
|
||||
47,
|
||||
47,
|
||||
72,
|
||||
97,
|
||||
122,
|
||||
147,
|
||||
],
|
||||
};
|
||||
|
||||
/// The alpha value that is used to draw the partially revealed ticks.
|
||||
const int _partiallyRevealedAlpha = 147;
|
||||
|
||||
class _CupertinoActivityIndicatorPainter extends CustomPainter {
|
||||
|
@ -145,9 +193,16 @@ class _CupertinoActivityIndicatorPainter extends CustomPainter {
|
|||
@required this.activeColor,
|
||||
@required this.radius,
|
||||
@required this.progress,
|
||||
}) : tickFundamentalRRect = RRect.fromLTRBXY(
|
||||
CupertinoActivityIndicatorIOSVersionStyle iOSVersionStyle =
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS13,
|
||||
}) : alphaValues = _kAlphaValuesMap[iOSVersionStyle],
|
||||
tickFundamentalRRect = RRect.fromLTRBXY(
|
||||
-radius / _kDefaultIndicatorRadius,
|
||||
-radius / 2.0,
|
||||
-radius /
|
||||
(iOSVersionStyle ==
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS14
|
||||
? 3.0
|
||||
: 2.0),
|
||||
radius / _kDefaultIndicatorRadius,
|
||||
-radius,
|
||||
radius / _kDefaultIndicatorRadius,
|
||||
|
@ -156,25 +211,29 @@ class _CupertinoActivityIndicatorPainter extends CustomPainter {
|
|||
super(repaint: position);
|
||||
|
||||
final Animation<double> position;
|
||||
final RRect tickFundamentalRRect;
|
||||
final Color activeColor;
|
||||
final double radius;
|
||||
final double progress;
|
||||
|
||||
final List<int> alphaValues;
|
||||
final RRect tickFundamentalRRect;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint();
|
||||
final int tickCount = alphaValues.length;
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(size.width / 2.0, size.height / 2.0);
|
||||
|
||||
final int activeTick = (_kTickCount * position.value).floor();
|
||||
final int activeTick = (tickCount * position.value).floor();
|
||||
|
||||
for (int i = 0; i < _kTickCount * progress; ++i) {
|
||||
final int t = (i - activeTick) % _kTickCount;
|
||||
paint.color = activeColor.withAlpha(progress < 1 ? _partiallyRevealedAlpha : _alphaValues[t]);
|
||||
for (int i = 0; i < tickCount * progress; ++i) {
|
||||
final int t = (i - activeTick) % tickCount;
|
||||
paint.color = activeColor
|
||||
.withAlpha(progress < 1 ? _partiallyRevealedAlpha : alphaValues[t]);
|
||||
canvas.drawRRect(tickFundamentalRRect, paint);
|
||||
canvas.rotate(_kTwoPI / _kTickCount);
|
||||
canvas.rotate(_kTwoPI / tickCount);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
|
@ -182,6 +241,8 @@ class _CupertinoActivityIndicatorPainter extends CustomPainter {
|
|||
|
||||
@override
|
||||
bool shouldRepaint(_CupertinoActivityIndicatorPainter oldPainter) {
|
||||
return oldPainter.position != position || oldPainter.activeColor != activeColor || oldPainter.progress != progress;
|
||||
return oldPainter.position != position ||
|
||||
oldPainter.activeColor != activeColor ||
|
||||
oldPainter.progress != progress;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import '../rendering/mock_canvas.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Activity indicator animate property works', (WidgetTester tester) async {
|
||||
|
||||
testWidgets('Activity indicator animate property works',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildCupertinoActivityIndicator());
|
||||
expect(SchedulerBinding.instance.transientCallbackCount, equals(1));
|
||||
|
||||
|
@ -38,7 +38,10 @@ void main() {
|
|||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.white,
|
||||
child: const CupertinoActivityIndicator(animating: false, radius: 35),
|
||||
child: const CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
radius: 35,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -58,7 +61,10 @@ void main() {
|
|||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.black,
|
||||
child: const CupertinoActivityIndicator(animating: false, radius: 35),
|
||||
child: const CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
radius: 35,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -71,6 +77,60 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('Activity indicator with iOS14 style',
|
||||
(WidgetTester tester) async {
|
||||
final Key key = UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: MediaQuery(
|
||||
data: const MediaQueryData(platformBrightness: Brightness.light),
|
||||
child: RepaintBoundary(
|
||||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.white,
|
||||
child: const CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
radius: 35,
|
||||
iOSVersionStyle:
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
find.byKey(key),
|
||||
matchesGoldenFile('activityIndicator.iOS14.paused.light.png'),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: MediaQuery(
|
||||
data: const MediaQueryData(platformBrightness: Brightness.dark),
|
||||
child: RepaintBoundary(
|
||||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.black,
|
||||
child: const CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
radius: 35,
|
||||
iOSVersionStyle:
|
||||
CupertinoActivityIndicatorIOSVersionStyle.iOS14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
find.byKey(key),
|
||||
matchesGoldenFile('activityIndicator.iOS14.paused.dark.png'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Activity indicator 0% in progress', (WidgetTester tester) async {
|
||||
final Key key = UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
|
@ -79,7 +139,8 @@ void main() {
|
|||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.white,
|
||||
child: const CupertinoActivityIndicator.partiallyRevealed(progress: 0),
|
||||
child:
|
||||
const CupertinoActivityIndicator.partiallyRevealed(progress: 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -91,7 +152,8 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('Activity indicator 30% in progress', (WidgetTester tester) async {
|
||||
testWidgets('Activity indicator 30% in progress',
|
||||
(WidgetTester tester) async {
|
||||
final Key key = UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
|
@ -99,7 +161,9 @@ void main() {
|
|||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.white,
|
||||
child: const CupertinoActivityIndicator.partiallyRevealed(progress: 0.5),
|
||||
child: const CupertinoActivityIndicator.partiallyRevealed(
|
||||
progress: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -111,7 +175,8 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('Activity indicator 100% in progress', (WidgetTester tester) async {
|
||||
testWidgets('Activity indicator 100% in progress',
|
||||
(WidgetTester tester) async {
|
||||
final Key key = UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
|
@ -119,7 +184,8 @@ void main() {
|
|||
key: key,
|
||||
child: Container(
|
||||
color: CupertinoColors.white,
|
||||
child: const CupertinoActivityIndicator.partiallyRevealed(progress: 1),
|
||||
child:
|
||||
const CupertinoActivityIndicator.partiallyRevealed(progress: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue