Flutter 绘制具有 3 多种颜色和值的圆形边框

Flutter Draw a circle border with 3 multiple colors and values

如何绘制具有多个值的图表样式的圆形边框?还为圆圈中的每个值动态扩展以填充 100% 的圆圈设置动画?

动画可以由 TweenAnimationBuilder 处理,它将在构建时播放。 为了达到预期的效果,我们必须使用 customPainter。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TweenAnimationBuilder(
          duration: const Duration(seconds: 2),
          tween: Tween(begin: 0.0, end: 1.0),
          curve: Curves.easeOutCubic,
          builder: (BuildContext context, dynamic value, Widget child) {
            return CustomPaint(
              painter: OpenPainter(
                  totalQuestions: 300,
                  learned: 75,
                  notLearned: 75,
                  range: value),
            );
          },
        ),
      ),
    );
  }
}

class OpenPainter extends CustomPainter {
  final learned;
  final notLearned;
  final range;
  final totalQuestions;
  double pi = math.pi;

  OpenPainter({this.learned, this.totalQuestions, this.notLearned, this.range});
  @override
  void paint(Canvas canvas, Size size) {
    double strokeWidth = 7;
    Rect myRect = const Offset(-50.0, -50.0) & const Size(100.0, 100.0);
    
    var paint1 = Paint()
      ..color = Colors.red
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;
    var paint2 = Paint()
      ..color = Colors.green
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;
    var paint3 = Paint()
      ..color = Colors.yellow
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;

    double firstLineRadianStart = 0;
    double _unAnswered = (totalQuestions - notLearned - learned) * range / totalQuestions;
    double firstLineRadianEnd = (360 * _unAnswered) * math.pi / 180;
    canvas.drawArc(
        myRect, firstLineRadianStart, firstLineRadianEnd, false, paint1);

    double _learned = (learned) * range / totalQuestions;
    double secondLineRadianEnd = getRadians(_learned);
    canvas.drawArc(myRect, firstLineRadianEnd, secondLineRadianEnd, false, paint2);
    double _notLearned = (notLearned) * range / totalQuestions;
    double thirdLineRadianEnd = getRadians(_notLearned);
    canvas.drawArc(myRect, firstLineRadianEnd + secondLineRadianEnd, thirdLineRadianEnd, false, paint3);

    // drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
  }

  double getRadians(double value) {
    return (360 * value) * pi / 180;
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

希望有人会觉得这很有帮助 :) 随时改进它!编码愉快!

感谢 Paulius Greičiūnas 的回答,我实现了一种更通用的方法来用不同的颜色绘制圆圈。您只需将颜色的出现指定为地图和圆的大小。

class MultipleColorCircle extends StatelessWidget {
  final Map<Color, int> colorOccurrences;
  final double height;
  final Widget? child;
  @override
  MultipleColorCircle(
      {required this.colorOccurrences, this.height = 20, this.child});
  Widget build(BuildContext context) => Container(
        height: height,
        width: height,
        child: CustomPaint(
            size: Size(20, 20),
            child: Center(child: child),
            painter: _MultipleColorCirclePainter(
              colorOccurrences: colorOccurrences,
              height: height,
            )),
      );
}

class _MultipleColorCirclePainter extends CustomPainter {
  final Map<Color, int> colorOccurrences;
  final double height;
  @override
  _MultipleColorCirclePainter(
      {required this.colorOccurrences, required this.height});
  double pi = math.pi;

  @override
  void paint(Canvas canvas, Size size) {
    double strokeWidth = 1;
    Rect myRect =
        Rect.fromCircle(center: Offset(height / 2, height / 2), radius: height);

    double radianStart = 0;
    double radianLength = 0;
    int allOccurrences = 0;
    //set denominator
    colorOccurrences.forEach((color, occurrence) {
      allOccurrences += occurrence;
    });
    colorOccurrences.forEach((color, occurrence) {
      double percent = occurrence / allOccurrences;
      radianLength = 2 * percent * math.pi;
      canvas.drawArc(
          myRect,
          radianStart,
          radianLength,
          false,
          Paint()
            ..color = color
            ..strokeWidth = strokeWidth
            ..style = PaintingStyle.stroke);
      radianStart += radianLength;
    });
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

使用地图,例如{Colors.blue: 2, Colors.green: 1} 你会得到一个 1/3 绿色和 2/3 蓝色的圆圈。 请注意,您还可以定义一个子项,以便圆圈中包含内容。这是我在日历中使用的一个示例,其中包含多个包含内容的圈子。