简体   繁体   中英

Flutter circular progress indicator - repeatable-Custom painter

I want to create something like that: Want to achieve

I have achieved this: Done up until now

I am struggling to add just vertical line at state of this circular progress bar just like the line at trailing.

import 'dart:math';

import 'package:flutter/material.dart';

class LoaderPaint extends CustomPainter {
  final double percentage;
  LoaderPaint({
    required this.percentage,
  });

  deg2Rand(double deg) => deg * pi / 180;

  @override
  void paint(Canvas canvas, Size size) {
    final midOffset = Offset(size.width / 2, size.height / 2);

    final paint = Paint()
      ..strokeCap = StrokeCap.round
      ..color = Colors.white
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawLine(
      Offset(midOffset.dy, 10),
      Offset(midOffset.dy,-10),
      paint,
    );
    canvas.drawArc(
      Rect.fromCenter(center: midOffset, width: size.width, height: size.height),
      deg2Rand(-90),
      deg2Rand(360 * percentage),
      false,
      paint,
    );

  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
  1. Calculate the end position of the line by using the formula found in this stackoverflow answer . Since we start the angle at -90 degrees we must take that away from the sweepAngle.

  2. Calculate the points 10 units before and after the end position of the line using the formula in this answer , plugging in the center of the circle and the end position of the line.

  3. Draw the line with canvas.drawLine

Here is what your updated LoaderPaint class looks like with these changes:

class LoaderPaint extends CustomPainter {
  final double percentage;
  const LoaderPaint({
    required this.percentage,
  });

  deg2Rand(double deg) => deg * pi / 180;

  @override
  void paint(Canvas canvas, Size size) {
    final radius = size.width / 2;
    final sweepAngle = deg2Rand(360 * percentage);
    final theta = deg2Rand(-90) + sweepAngle;
    
    final midOffset = Offset(radius, radius);
    final endOffset = Offset(radius + radius * cos(theta), radius + radius * sin(theta));
    
    final midEndDiff = sqrt(pow(endOffset.dx - midOffset.dx, 2) + pow(endOffset.dy - midOffset.dy, 2));

    final paint = Paint()
      ..strokeCap = StrokeCap.round
      ..color = Colors.white
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawLine(
      Offset(midOffset.dy, 10),
      Offset(midOffset.dy,-10),
      paint,
    );
    canvas.drawArc(
      Rect.fromCenter(center: midOffset, width: size.width, height: size.height),
      deg2Rand(-90),
      sweepAngle,
      false,
      paint,
    );
    canvas.drawLine(
      Offset(endOffset.dx + (10/midEndDiff) * (endOffset.dx - midOffset.dx), endOffset.dy + (10/midEndDiff) * (endOffset.dy - midOffset.dy)),
      Offset(endOffset.dx - (10/midEndDiff) * (endOffset.dx - midOffset.dx), endOffset.dy - (10/midEndDiff) * (endOffset.dy - midOffset.dy)),
      paint,
    );

  }

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

This dartpad shows it working. You can easily change the percentage to see it work at all angles.

You must bear in mind this will only work if the width and height of the CustomPaint widget are exactly the same, however your example without the end cap would also break if the width and height were different.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM