Flutter 圆环占比图,统计百分数自定义 Widget

最近使用 Flutter 开发 App 时,做了一个圆环占比图,或者说统计图。但现在 UI 又把设计修改了,当时实现的控件被废弃没啥用。但我觉得这个圆环风格统计图设计的挺好看,万一后面还需要担心找不到,所以贴到博客来,可能还能帮到有需要的朋友。

圆环占比统计图 Widget 控件具体实现代码

import 'dart:math' as math;

/// 圆环占比统计图
class CustomCircle extends StatelessWidget {
  final double offline, online, fault;
  final String topText, bottomText;
  final double size;
  final Color offlineColor, onlineColor, faultColor;

  CustomCircle({
    this.offline = 0,
    this.online = 0,
    this.fault = 0,
    this.topText = '',
    this.bottomText = '',
    this.size = 140,
    this.offlineColor = Colors.grey,
    this.onlineColor = Colors.green,
    this.faultColor = Colors.red,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return Stack(
          children: [
            SizedBox(
              height: size,
              width: size,
              child: CustomPaint(
                painter: CirclePainter(offline, online, fault, offlineColor, onlineColor, faultColor),
              ),
            ),
            Positioned(
              top: size / 3,
              child: Container(
                width: size,
                child: Text(topText, textAlign: TextAlign.center, style: Theme.of(context).textTheme.subtitle1,),
              ),
            ),
            Positioned(
              top: size / 2,
              child: Container(
                width: size,
                child: Text(bottomText, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyText1?.copyWith(fontWeight: FontWeight.bold,)),
              ),
            ),
          ],
        );
      },
    );
  }
}

class CirclePainter extends CustomPainter {
  final double offline, online, fault;
  final Color offlineColor, onlineColor, faultColor;

  CirclePainter(
      this.offline,
      this.online,
      this.fault,
      this.offlineColor,
      this.onlineColor,
      this.faultColor,
      );

  @override
  void paint(Canvas canvas, Size size) {
    double startAngle = -math.pi / 2;
    double total = offline + online + fault;

    Paint offlinePaint = Paint()
      ..color = offlineColor
      ..strokeWidth = 14
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Paint onlinePaint = Paint()
      ..color = onlineColor
      ..strokeWidth = 14
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Paint faultPaint = Paint()
      ..color = faultColor
      ..strokeWidth = 14
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    canvas.drawCircle(size.center(Offset.zero), size.width / 2, offlinePaint);
    if (online > 0) {
      double sweepAngle = online / total * 2 * math.pi;
      canvas.drawArc(
          Rect.fromCircle(center: size.center(Offset.zero), radius: size.width / 2),
          startAngle,
          sweepAngle,
          false,
          onlinePaint);
    }
    if (fault > 0) {
      double sweepAngle = fault / total * 2 * math.pi;
      canvas.drawArc(
          Rect.fromCircle(center: size.center(Offset.zero), radius: size.width / 2),
          startAngle,
          sweepAngle,
          false,
          faultPaint);
    }
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) => false;
}

使用

  Widget buildCirclePercent(BuildContext context, int offline, int online, int fault) {
    return Container(
      width: double.infinity,
      padding: EdgeInsets.symmetric(vertical: 20),
      child: Center(
        child: CustomCircle(
          offline: offline.toDouble(),
          online: online.toDouble(),
          fault: fault.toDouble(),
          topText: '总数',
          bottomText: '${offline+online+fault}',
          size: 148,
          offlineColor: Colors.grey,
          onlineColor: Colors.green,
          faultColor: Colors.red,
        ),
      ),
    );
  }

上面图片中我画了三个,分别代表不同情况的展示。

 @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CustomCircle Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: Text("CustomCircle Demo")),
        body: Column(
          children: [
            buildCirclePercent(context, 10, 20, 5),
            buildCirclePercent(context, 6, 6, 0),
            buildCirclePercent(context, 0, 0, 0),
          ],
        ),  // 使用了示例数字
      ),
    );
  }

OK,希望以上内容能帮到你。

本文由老郭种树原创,转载请注明:https://guozh.net/flutter-ring-proportion-chart/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注