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

如何在Flutter中自定义进度指示器?求Figma设计还原方案

自定义Flutter进度指示器实现方案

针对你需要还原的Figma设计进度指示器,以下提供两种自定义实现方案(无需依赖第三方包),可根据设计样式选择适配:

方案一:横向分段进度指示器

适合线性步骤类的进度展示,包含节点状态、进度条和步骤标题,可精准匹配设计细节:

import 'package:flutter/material.dart';

class SegmentProgressIndicator extends StatelessWidget {
  final int totalSteps;
  final int currentStep;
  final Color completedColor;
  final Color activeColor;
  final Color inactiveColor;

  const SegmentProgressIndicator({
    super.key,
    required this.totalSteps,
    required this.currentStep,
    this.completedColor = Colors.green,
    this.activeColor = Colors.blue,
    this.inactiveColor = Colors.grey.shade300,
  }) : assert(currentStep >= 1 && currentStep <= totalSteps, "当前步骤必须在1到总步骤之间");

  @override
  Widget build(BuildContext context) {
    return Row(
      children: List.generate(totalSteps, (index) {
        final step = index + 1;
        final isCompleted = step < currentStep;
        final isActive = step == currentStep;
        final isInactive = step > currentStep;

        return Expanded(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 进度节点
              Container(
                width: 24,
                height: 24,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: isCompleted ? completedColor : isActive ? activeColor : inactiveColor,
                  border: isInactive ? Border.all(color: inactiveColor) : null,
                ),
                child: isCompleted
                    ? const Icon(Icons.check, color: Colors.white, size: 16)
                    : Text(
                        step.toString(),
                        style: TextStyle(color: isActive ? Colors.white : inactiveColor),
                      ),
              ),
              const SizedBox(height: 8),
              // 步骤标题(可根据设计删除或修改)
              Text(
                "步骤$step",
                style: TextStyle(
                  color: isCompleted || isActive ? Colors.black87 : inactiveColor,
                  fontSize: 12,
                ),
              ),
            ],
          ),
        );
      }).expand((widget) {
        final index = (widget.key as ValueKey<int>).value;
        // 添加节点间的进度条
        if (index < totalSteps) {
          return [
            widget,
            Expanded(
              child: Container(
                height: 2,
                color: (index + 1) < currentStep
                    ? completedColor
                    : (index + 1) == currentStep
                        ? activeColor
                        : inactiveColor,
              ),
            ),
          ];
        }
        return [widget];
      }).toList(),
    );
  }
}

// 使用示例
class ProgressDemo extends StatelessWidget {
  const ProgressDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("分段进度指示器")),
      body: const Padding(
        padding: EdgeInsets.all(20.0),
        child: SegmentProgressIndicator(
          totalSteps: 4,
          currentStep: 2,
        ),
      ),
    );
  }
}

方案二:环形分段进度指示器

适合环形类的进度展示,支持分段填充、中心文字显示:

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

class CircularSegmentProgress extends CustomPainter {
  final int totalSegments;
  final int currentSegment;
  final Color completedColor;
  final Color activeColor;
  final Color inactiveColor;
  final double strokeWidth;

  CircularSegmentProgress({
    required this.totalSegments,
    required this.currentSegment,
    this.completedColor = Colors.green,
    this.activeColor = Colors.blue,
    this.inactiveColor = Colors.grey.shade300,
    this.strokeWidth = 8.0,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;
    final segmentAngle = 2 * math.pi / totalSegments;

    // 绘制未完成分段
    for (int i = currentSegment; i < totalSegments; i++) {
      final startAngle = i * segmentAngle - math.pi / 2;
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        startAngle,
        segmentAngle,
        false,
        Paint()
          ..color = inactiveColor
          ..strokeWidth = strokeWidth
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round,
      );
    }

    // 绘制已完成分段
    for (int i = 0; i < currentSegment - 1; i++) {
      final startAngle = i * segmentAngle - math.pi / 2;
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        startAngle,
        segmentAngle,
        false,
        Paint()
          ..color = completedColor
          ..strokeWidth = strokeWidth
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round,
      );
    }

    // 绘制当前活跃分段
    if (currentSegment <= totalSegments) {
      final startAngle = (currentSegment - 1) * segmentAngle - math.pi / 2;
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        startAngle,
        segmentAngle,
        false,
        Paint()
          ..color = activeColor
          ..strokeWidth = strokeWidth
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round,
      );
    }

    // 绘制中心进度文字
    final textPainter = TextPainter(
      text: TextSpan(
        text: "$currentSegment/$totalSegments",
        style: const TextStyle(color: Colors.black87, fontSize: 16, fontWeight: FontWeight.bold),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(
      canvas,
      Offset(center.dx - textPainter.width / 2, center.dy - textPainter.height / 2),
    );
  }

  @override
  bool shouldRepaint(CircularSegmentProgress oldDelegate) {
    return oldDelegate.currentSegment != currentSegment ||
        oldDelegate.totalSegments != totalSegments;
  }
}

// 使用示例
class CircularProgressDemo extends StatelessWidget {
  const CircularProgressDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("环形分段进度指示器")),
      body: const Center(
        child: SizedBox(
          width: 120,
          height: 120,
          child: CustomPaint(
            painter: CircularSegmentProgress(
              totalSegments: 4,
              currentSegment: 2,
            ),
          ),
        ),
      ),
    );
  }
}

自定义适配说明

  • 可修改completedColor、activeColor、节点尺寸、图标、文字样式等参数,完全匹配Figma设计
  • 若设计有特殊交互(如点击节点切换进度),可在组件内添加GestureDetector实现

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 14:49:57