You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现按钮样式的环形饼图?

Flutter 实现按钮式环形饼图

如果现成的饼图包满足不了需求,用CustomPainter自定义绘制是最灵活的方案,以下是适配你需求的实现代码:

自定义环形饼图绘制类

import 'package:flutter/material.dart';

class RingButtonPainter extends CustomPainter {
  final double progress; // 进度值(0-1)
  final Color ringColor; // 环形底色
  final Color progressColor; // 进度环颜色
  final bool isSelected; // 是否选中状态

  RingButtonPainter({
    required this.progress,
    required this.ringColor,
    required this.progressColor,
    required this.isSelected,
  });

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

    // 绘制底层环形背景
    final backgroundPaint = Paint()
      ..color = ringColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    canvas.drawCircle(center, radius - strokeWidth / 2, backgroundPaint);

    // 绘制进度环(选中状态才显示)
    if (isSelected) {
      final progressPaint = Paint()
        ..color = progressColor
        ..style = PaintingStyle.stroke
        ..strokeWidth = strokeWidth
        ..strokeCap = StrokeCap.round;
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius - strokeWidth / 2),
        -0.5 * 3.1416, // 从顶部开始绘制
        progress * 2 * 3.1416, // 进度对应的弧度
        false,
        progressPaint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant RingButtonPainter oldDelegate) {
    return oldDelegate.progress != progress ||
        oldDelegate.isSelected != isSelected ||
        oldDelegate.ringColor != ringColor ||
        oldDelegate.progressColor != progressColor;
  }
}

封装成可交互的按钮组件

class RingButton extends StatefulWidget {
  final IconData icon;
  final double initialProgress;
  final Color ringColor;
  final Color progressColor;
  final Color iconColor;
  final VoidCallback? onTap;

  const RingButton({
    super.key,
    required this.icon,
    this.initialProgress = 0.0,
    this.ringColor = Colors.grey.shade300,
    this.progressColor = Colors.blue,
    this.iconColor = Colors.black,
    this.onTap,
  });

  @override
  State<RingButton> createState() => _RingButtonState();
}

class _RingButtonState extends State<RingButton> {
  bool _isSelected = false;
  double _progress = 0.0;

  @override
  void initState() {
    super.initState();
    _progress = widget.initialProgress;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {
          _isSelected = !_isSelected;
          // 模拟进度变化,实际可根据业务逻辑设置具体值
          _progress = _isSelected ? 0.75 : 0.0;
        });
        widget.onTap?.call();
      },
      child: Container(
        width: 60,
        height: 60,
        child: Stack(
          alignment: Alignment.center,
          children: [
            CustomPaint(
              painter: RingButtonPainter(
                progress: _progress,
                ringColor: widget.ringColor,
                progressColor: widget.progressColor,
                isSelected: _isSelected,
              ),
              size: Size.infinite,
            ),
            Icon(
              widget.icon,
              color: _isSelected ? widget.progressColor : widget.iconColor,
              size: 28,
            ),
          ],
        ),
      ),
    );
  }
}

使用示例

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: [
    RingButton(icon: Icons.home),
    RingButton(icon: Icons.favorite, progressColor: Colors.red),
    RingButton(icon: Icons.settings, progressColor: Colors.green),
  ],
)

你可以根据需求调整strokeWidth、尺寸、颜色、进度值,以及点击后的状态变化逻辑,完全匹配你需要的按钮式环形饼图样式。

内容的提问来源于stack exchange,提问作者DholaSain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 15:09:24