Marwin Lebensky
Marwin Lebensky

Reputation: 421

CircularProgressIndicator with Dividers for Milestones/Steps (e.g. 1/3, 2/3, 3/3)

Hello Flutter Community,

i want to create this custom CircularProgressIndicator:

CircularProgressIndicator with steps

For the text I built a stack:

Stack(
  children: [
    Container(
      height: 75.0,
      width: 75.0,
      child: CircularProgressIndicator(
        strokeWidth: 6.0,
        backgroundColor: Color.fromRGBO(230, 230, 230, 1.0),
        valueColor: AlwaysStoppedAnimation<Color>(
            Color.fromRGBO(252, 90, 73, 1.0)),
        value: 0.4,
      ),
    ),
    Positioned(
      child: Container(
        height: 75.0,
        width: 75.0,
        child: Center(
          child: Text("2 of 6"),
        ),
      ),
    ),
  ],
),

I thought of drawing six lines with +60° (amount-of-steps/360°) rotation from the center to the end, but I don't know how to do so.

enter image description here

Is there an elegant way of achieving this?

Cheers from germany

Edit (1): I have found a solution, but i'm not happy with it:



Stack(
  children: [
    Container(
      height: 75.0,
      width: 75.0,
      child: CircularProgressIndicator(
        strokeWidth: 6.0,
        backgroundColor: Color.fromRGBO(230, 230, 230, 1.0),
        valueColor: AlwaysStoppedAnimation<Color>(
            Color.fromRGBO(252, 90, 73, 1.0)),
        value: 2/6,
      ),
    ),
    Positioned(
      height: 75.0,
      width: 75.0,
      child: Center(
        child: CustomPaint(
          painter: DrawRadialLines(
              amount: 6,
              thickness: 4,
              length: 75,
              color: Colors.white),
        ),
      ),
    ),
    Positioned(
      child: Container(
        height: 75.0,
        width: 75.0,
        child: Center(
          child: Text("2 of 6"),
        ),
      ),
    ),
  ],
),

And this Class DrawRadialLines:

class DrawRadialLines extends CustomPainter {
  Paint _paint;
  final double thickness;
  final double amount;
  final double length;
  final Color color;
  DrawRadialLines({this.thickness, this.amount, this.length, this.color}) {
    _paint = Paint()
      ..color = this.color
      ..strokeWidth = this.thickness
      ..strokeCap = StrokeCap.round;
  }

  @override
  void paint(Canvas canvas, Size size) {
    final double _strokeThickness = thickness;
    final double _rotationPerLine = 360 / amount;
    final double _length = length/2 + _strokeThickness;
    canvas.rotate(-90 * (3.1415926 / 180)); //rotate so it will start from top
    for (int i = 0; i < amount; i++) {
      canvas.rotate(_rotationPerLine * (3.1415926 / 180));
      canvas.drawLine(Offset(0, 0.0), Offset(_length, 0.0), _paint);
    }
  }

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

it will output this: enter image description here

However, is there a more elegant solution?

Upvotes: 1

Views: 609

Answers (1)

F Perroch
F Perroch

Reputation: 2215

You can add separators to a new Stack like this :

First, add the AlignmentDirectional.centerto your stack and add the new Stack to your existing Stack

Stack(
  alignment: AlignmentDirectional.center,
  children: [
    Container(
      height: 75.0,
      width: 75.0,
      child: CircularProgressIndicator(
        strokeWidth: 6.0,
        backgroundColor: Color.fromRGBO(230, 230, 230, 1.0),
        valueColor: AlwaysStoppedAnimation<Color>(Color.fromRGBO(252, 90, 73, 1.0)),
        value: 0.4,
      ),
    ),
    Positioned(
      child: Container(
        height: 75.0,
        width: 75.0,
        child: Center(
          child: Text("2 of 6"),
        ),
      ),
    ),
    Stack(
      children: buildSeparators(6),
    ),
  ],
),

and there is the buildSeparators method

List<Widget> buildSeparators(int nbSeparators) {
  var sep = <Widget>[];
  for (var i = 0; i < nbSeparators; i++) {
    sep.add(
      Transform.rotate(
        angle: i * 1 / nbSeparators * 2 * pi,
        child: Container(
          width: 10,
          height: 81,
          child: Column(
            children: <Widget>[
              Container(
                width: 8,
                height: 10,
                color: Colors.white,
              )
            ],
          ),
        ),
      ),
    );
  }
  return sep;
}

It can be improved but that's the idea

Upvotes: 1

Related Questions