How to create gradient progress indicator like this?

Viewed 3359

I noticed that the basic CircularProgressIndicator widget has very few parameters to customize it. I would like to achieve a result like on the gif. Unfortunately, my knowledge isn't enough to create such an indicator from scratch, searches on pub.dev didn't bring any results.

enter image description here

1 Answers
  • Make a CustomPainter to draw the circle. Use SweepGradient(...).createShader(...) to apply the gradient effect.

  • Wrap the widget with a RotationTransition to make the widget spin.

  • Make an animation for spinning the widget.

Code:

The circular progress indicator widget:

class GradientCircularProgressIndicator extends StatelessWidget {
  final double radius;
  final List<Color> gradientColors;
  final double strokeWidth;

  GradientCircularProgressIndicator({
    @required this.radius,
    @required this.gradientColors,
    this.strokeWidth = 10.0,
  });

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size.fromRadius(radius),
      painter: GradientCircularProgressPainter(
        radius: radius,
        gradientColors: gradientColors,
        strokeWidth: strokeWidth,
      ),
    );
  }
}

class GradientCircularProgressPainter extends CustomPainter {
  GradientCircularProgressPainter({
    @required this.radius,
    @required this.gradientColors,
    @required this.strokeWidth,
  });
  final double radius;
  final List<Color> gradientColors;
  final double strokeWidth;

  @override
  void paint(Canvas canvas, Size size) {
    size = Size.fromRadius(radius);
    double offset = strokeWidth / 2;
    Rect rect = Offset(offset, offset) &
        Size(size.width - strokeWidth, size.height - strokeWidth);
    var paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth;
    paint.shader =
        SweepGradient(colors: gradientColors, startAngle: 0.0, endAngle: 2 * pi)
            .createShader(rect);
    canvas.drawArc(rect, 0.0, 2 * pi, false, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

Animation controller:

AnimationController _animationController;

  @override
  void initState() {
    _animationController =
        new AnimationController(vsync: this, duration: Duration(seconds: 1));
    _animationController.addListener(() => setState(() {}));
    _animationController.repeat();
    super.initState();
  }

Usage:

RotationTransition(
  turns: Tween(begin: 0.0, end: 1.0).animate(_animationController),
  child: GradientCircularProgressIndicator(
    radius: 50,
    gradientColors: [
      Colors.white,
      Colors.red,
    ],
    strokeWidth: 10.0,
  ),
),

Result:

res

Related