Let’s start with the renderings

Implementation steps

Draw progress bar

  1. First draw the wave
	    void drawWave(Canvas canvas, Offset center, double radius,
	        double waveOffsetPercent, Paint paint) {
	      double waveOffset = -(waveOffsetPercent * radius * 2);
		  // Circle the canvascanvas.save(); Path clipPath = Path() .. addOval(Rect.fromCircle(center: center, radius: radius)); canvas.clipPath(clipPath);// Represent point(p) and controlPoint (C) as shown above
	      double waveProgressHeightY = (1 - percent) * radius * 2;
	      Offset point1 = Offset(waveOffset, waveProgressHeightY);
	      Offset point2 = Offset(waveOffset + radius, waveProgressHeightY);
	      Offset point3 = Offset(waveOffset + radius * 2, waveProgressHeightY);
	      Offset point4 = Offset(waveOffset + radius * 3, waveProgressHeightY);
	      Offset point5 = Offset(waveOffset + radius * 4, waveProgressHeightY);
	      Offset point6 = Offset(point5.dx, radius * 2 + halfWaveHeight);
	      Offset point7 = Offset(point1.dx, radius * 2 + halfWaveHeight);
	      Offset controlPoint1 =
	          Offset(waveOffset + radius * 0.5, waveProgressHeightY - halfWaveHeight);
	      Offset controlPoint2 =
	          Offset(waveOffset + radius * 1.5, waveProgressHeightY + halfWaveHeight);
	      Offset controlPoint3 =
	          Offset(waveOffset + radius * 2.5, waveProgressHeightY - halfWaveHeight);
	      Offset controlPoint4 =
	          Offset(waveOffset + radius * 3.5, waveProgressHeightY + halfWaveHeight);
		  // Complete the path linkPath wavePath = Path() .. moveTo(point1.dx, point1.dy) .. quadraticBezierTo( controlPoint1.dx, controlPoint1.dy, point2.dx, point2.dy) .. quadraticBezierTo( controlPoint2.dx, controlPoint2.dy, point3.dx, point3.dy) .. quadraticBezierTo( controlPoint3.dx, controlPoint3.dy, point4.dx, point4.dy) .. quadraticBezierTo( controlPoint4.dx, controlPoint4.dy, point5.dx, point5.dy) .. lineTo(point6.dx, point6.dy) .. lineTo(point7.dx, point7.dy) .. close();// Finish drawing
	      canvas.drawPath(wavePath, paint);
	      canvas.restore();
	    }
Copy the code
  1. Draw a cascade of waves, using the same method as in the first step, by stagging the colors and offsets from the first wave

  2. Draw the content of circular progress. What needs to be noted in this step is that the canvas needs to be rotated when drawing the progress. The specific content is as follows

    Void drawCircleProgress(Canvas Canvas, Offset center, double radius, Size Size) {Canvas. DrawCircle (center, radius, circleProgressBGPaint); // Save the canvas state canvas.save(); Rotate (degreeToRadian(-90)); canvas.translate( -(size.height + size.width) / 2, -(size.height - size.width) / 2); Canvas. DrawArc (rect. fromCircle(center: center, radius: radius), degreeToRadian(0), degreeToRadian(percent * 360), false, circleProgressPaint); // Restore the canvas state canvas.restore(); }Copy the code

The drawing is almost done

Get the waves moving

Use animation to change the offset of the wave and make the animation repeat over and over again. The offset speeds of the two waves are set to be inconsistent to make the waves look more coordinated

	  void initState() {
	    super.initState();
	    waveAnimation = AnimationController(
	      vsync: this,
	      duration: widget.waveAnimationDuration,
	    );
	    waveAnimation.addListener(waveAnimationListener);
	    lightWaveAnimation = AnimationController(
	      vsync: this,
	      duration: widget.lightWaveAnimationDuration,
	    );
		/ / for the latest wave in lightWaveAnimationListener offset value, refresh the status
	    lightWaveAnimation.addListener(lightWaveAnimationListener);
	    waveAnimation.repeat();
	    lightWaveAnimation.repeat();
	  }
Copy the code

After completing the effect of the wave moving, it is almost done, but there is an obvious problem, that is, after setting the progress, the wave in the progress bar will rise instantly, which looks very incongruous, so we need to add an animation effect for the progress bar when the progress changes.

Let the waves rise and fall slowly

	  @override
	  void initState() {
	    super.initState();
	    controller = AnimationController(
	        vsync: this, duration: widget.progressAnimatedDuration);
	    controller.addStatusListener((status) {
		  // Reset the animation when the animation ends
	      if(status == AnimationStatus.completed) { progressAnimation.removeListener(handleProgressChange); controller.reset(); }}); }@override
	  Widget build(BuildContext context) {
		// Start animation when progress changes
	    if(currentValue ! = widget.value && ! controller.isAnimating) { progressAnimation = controller.drive(IntTween(begin: currentValue, end: widget.value)); progressAnimation.addListener(handleProgressChange); controller.forward(); }... }Copy the code

When the progress changes, we use an animation to change the progress to the specified progress, which ensures that the wave does not rise immediately when the progress changes.

The project address