这篇文章主要是通过Canvas实现一个圆形渐变进度条。主要功能:
- 支持多种背景渐变色
- 任意弧度,进度条可以不是圆形
- 可以自定义粗细、两端是否圆角等样式
示例:
class SSLGradientCircularProgressIndicator extends StatelessWidget{
//半径
final double radius;
//两端是否圆角
final bool strokeCapRound;
//当前进度
final double value;
//粗细
final double strokeWidth;
//进度条背景色
final Color backgroundColor;
//进度条总弧度,2*PI为整圆,小于2*PI为圆弧
final double totalAngle;
//渐变色数组
final List<Color> colors;
//渐变色的终止点,对应colors属性
final List<double>? stops;
const SSLGradientCircularProgressIndicator({
Key? key,
this.strokeWidth = 2.0,
required this.radius,
required this.colors,
this.stops,
this.strokeCapRound = false,
this.backgroundColor = Colors.white,
this.totalAngle = 2*pi,
this.value = 0,
}):super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
double offset = 0.0;
if (strokeCapRound){
offset = asin(strokeWidth/(radius*2 - strokeWidth));
}
var colorsTem = colors;
// if (colorsTem == null){
// Color color = Theme.of(context).colorScheme.secondary;
// colorsTem = [color, color];
// }
return Transform.rotate(
angle: -pi/2.0 - offset,
child: CustomPaint(
size: Size.fromRadius(radius),
painter: SSLGradientCircularProgressPainter(
strokeWidth: strokeWidth,
strokeCapRound: strokeCapRound,
backgroundColor: backgroundColor,
value: value,
total: totalAngle,
radius: radius,
colors: colors,
stops: stops,
),
),
);
}
}
class SSLGradientCircularProgressPainter extends CustomPainter{
final double strokeWidth;
final bool strokeCapRound;
final double value;
final Color backgroundColor;
final List<Color> colors;
final double total;
final double? radius;
final List<double>? stops;
SSLGradientCircularProgressPainter({
this.strokeWidth = 10.0,
this.strokeCapRound = true,
this.backgroundColor = Colors.white,
this.radius,
this.total = 2*pi,
this.stops,
this.value = 0,
required this.colors,
});
@override
void paint(Canvas canvas, Size size) {
if (radius != null){
size = Size.fromRadius(radius!);
}
double offset = strokeWidth/2.0;
double start = 0.0;
if (strokeCapRound){
start = asin(strokeWidth / (size.width - strokeWidth));
}
Rect rect = Offset(offset, offset) & Size(size.width - strokeWidth, size.height - strokeWidth);
var paint = Paint()
..strokeCap = strokeCapRound ? StrokeCap.round : StrokeCap.butt
..style = PaintingStyle.stroke
..isAntiAlias = true
..strokeWidth = strokeWidth;
//画背景色
if (backgroundColor != Colors.transparent){
paint.color = backgroundColor;
canvas.drawArc(rect, start, total, false, paint);
}
//画前景,应用渐变
if (value > 0){
paint.shader = SweepGradient(
colors: colors,
startAngle: 0.0,
endAngle: value,
stops: stops,
).createShader(rect);
canvas.drawArc(rect, start, value, false, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
class SSLGradientCircularProgressRoute extends StatefulWidget{
const SSLGradientCircularProgressRoute({Key? key}):super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return SSLGradientCircularProgressRouteState();
}
}
class SSLGradientCircularProgressRouteState extends State<SSLGradientCircularProgressRoute> with TickerProviderStateMixin{
late AnimationController animationController;
@override
void initState() {
// TODO: implement initState
super.initState();
animationController = AnimationController(vsync: this,duration: const Duration(seconds: 3));
bool isForward = true;
animationController.addStatusListener((status) {
if (status == AnimationStatus.completed || status == AnimationStatus.dismissed){
if (isForward){
animationController.reverse();
}else{
animationController.forward();
}
}else{
isForward = false;
}
});
animationController.forward();
}
@override
void dispose() {
// TODO: implement dispose
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: const Text("SSL GradientCircular"),
),
body: SingleChildScrollView(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedBuilder(animation: animationController, builder: (BuildContext context, child){
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
children: [
Wrap(
spacing: 10.0,
runSpacing: 16.0,
children: [
SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: const [Colors.blue, Colors.red],
strokeWidth: 3.0,
value: animationController.value,
),
SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: const [Colors.yellow, Colors.orange],
strokeWidth: 3,
value: animationController.value,
),
SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: const [Colors.purple, Colors.deepPurpleAccent],
strokeWidth: 3,
value: CurvedAnimation(
parent: animationController,
curve: Curves.decelerate
).value,
),
SSLTurnBox(
turns: 1 / 8,
child: SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: const [Colors.blue, Colors.red],
strokeWidth: 3,
value: CurvedAnimation(
parent: animationController,
curve: Curves.ease
).value,
),
),
RotatedBox(
quarterTurns: 1,
child: SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: [Colors.blue.shade700, Colors.red.shade100],
strokeWidth: 3,
backgroundColor: Colors.transparent,
value: animationController.value,
),
),
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.5,
child: Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: SizedBox(
child: SSLTurnBox(
turns: 0.75,
child: SSLGradientCircularProgressIndicator(
radius: 100.0,
colors: const [Colors.teal, Colors.cyan],
strokeWidth: 8,
totalAngle: pi,
value: animationController.value,
),
),
),
),
),
),
SizedBox(
height: 104,
width: 200,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
height: 200,
top: .0,
child: SSLTurnBox(
turns: 0.75,
child: SSLGradientCircularProgressIndicator(
radius: 50.0,
colors: const [Colors.blue, Colors.red],
strokeWidth: 3,
value: animationController.value,
),
),
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Text(
"${(animationController.value*100).toInt()}",
style: const TextStyle(
fontSize: 25.0,
color: Colors.blueGrey
),
),
),
],
),
),
],
)
],
),
);
})
],
),
),
),
);
}
}