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

如何在Flutter中实现这种锯齿形进度指示器设计?

实现Flutter锯齿形进度指示器(含自定义完成对勾)

针对你需要的推进式锯齿进度指示器,我们可以通过CustomPainter精准实现,以下是完整的可复用方案:

核心绘制逻辑(CustomPainter)

创建自定义绘制类,处理锯齿路径、进度填充和完成对勾的绘制:

import 'package:flutter/material.dart';

class SawtoothProgressPainter extends CustomPainter {
  final int totalSteps;
  final int completedSteps;
  final Color completedColor;
  final Color incompleteColor;
  final Color checkmarkColor;
  final double toothHeight;
  final double toothWidth;

  SawtoothProgressPainter({
    required this.totalSteps,
    required this.completedSteps,
    this.completedColor = Colors.green,
    this.incompleteColor = Colors.grey.shade300,
    this.checkmarkColor = Colors.white,
    this.toothHeight = 20.0,
    this.toothWidth = 30.0,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..style = PaintingStyle.fill;
    final path = Path();

    // 绘制完整锯齿路径框架
    path.moveTo(0, size.height / 2);
    for (int i = 0; i < totalSteps; i++) {
      final x = i * toothWidth;
      path.lineTo(x + toothWidth / 2, size.height / 2 - toothHeight / 2);
      path.lineTo(x + toothWidth, size.height / 2 + toothHeight / 2);
    }
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);
    path.close();

    // 绘制未完成部分底色
    paint.color = incompleteColor;
    canvas.drawPath(path, paint);

    // 绘制已完成部分填充色
    final completedWidth = completedSteps * toothWidth;
    final clipRect = Rect.fromLTWH(0, 0, completedWidth, size.height);
    canvas.save();
    canvas.clipRect(clipRect);
    paint.color = completedColor;
    canvas.drawPath(path, paint);
    canvas.restore();

    // 绘制已完成步骤的自定义对勾
    final checkmarkPaint = Paint()
      ..color = checkmarkColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0
      ..strokeCap = StrokeCap.round;

    for (int i = 0; i < completedSteps; i++) {
      final centerX = i * toothWidth + toothWidth / 2;
      final centerY = size.height / 2;
      final checkmarkPath = Path()
        ..moveTo(centerX - 5, centerY)
        ..lineTo(centerX - 2, centerY + 4)
        ..lineTo(centerX + 6, centerY - 6);
      canvas.drawPath(checkmarkPath, checkmarkPaint);
    }
  }

  @override
  bool shouldRepaint(covariant SawtoothProgressPainter oldDelegate) {
    return oldDelegate.completedSteps != completedSteps ||
        oldDelegate.totalSteps != totalSteps;
  }
}

封装成可复用组件

将Painter包装成Widget,方便项目中直接调用:

class SawtoothProgressIndicator extends StatelessWidget {
  final int totalSteps;
  final int completedSteps;
  final double height;
  final Color completedColor;
  final Color incompleteColor;
  final Color checkmarkColor;

  const SawtoothProgressIndicator({
    super.key,
    required this.totalSteps,
    required this.completedSteps,
    this.height = 40.0,
    this.completedColor = Colors.green,
    this.incompleteColor = Colors.grey.shade300,
    this.checkmarkColor = Colors.white,
  }) : assert(completedSteps <= totalSteps, "完成步骤不能超过总步骤");

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: height,
      width: double.infinity,
      child: CustomPaint(
        painter: SawtoothProgressPainter(
          totalSteps: totalSteps,
          completedSteps: completedSteps,
          completedColor: completedColor,
          incompleteColor: incompleteColor,
          checkmarkColor: checkmarkColor,
          toothHeight: height * 0.5,
          toothWidth: (MediaQuery.of(context).size.width) / totalSteps,
        ),
      ),
    );
  }
}

使用示例

在页面中调用组件,控制进度推进:

class ProgressPage extends StatefulWidget {
  const ProgressPage({super.key});

  @override
  State<ProgressPage> createState() => _ProgressPageState();
}

class _ProgressPageState extends State<ProgressPage> {
  int _completedSteps = 2;
  final int _totalSteps = 5;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("锯齿进度指示器")),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SawtoothProgressIndicator(
              totalSteps: _totalSteps,
              completedSteps: _completedSteps,
              completedColor: Colors.blue,
            ),
            const SizedBox(height: 30),
            ElevatedButton(
              onPressed: () {
                if (_completedSteps < _totalSteps) {
                  setState(() => _completedSteps++);
                }
              },
              child: const Text("推进进度"),
            ),
          ],
        ),
      ),
    );
  }
}

细节调整说明

  • 锯齿的尺寸可通过参数动态修改,适配不同布局
  • 对勾路径可调整坐标点,匹配你设计的自定义样式
  • 通过shouldRepaint控制重绘时机,避免不必要的性能消耗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:42:49