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

如何制作带有自定义滑块的Flutter圆形进度指示器?

实现自定义滑块组件的方案

先看你要构建的目标组件:
目标滑块组件

针对你提到的Flutter原生无对应组件、未找到第三方库的情况,可以通过自定义组件来实现,以下是核心思路和代码示例:

核心实现逻辑

  • 用GestureDetector监听拖拽事件,获取滑动偏移量
  • 通过CustomPaint绘制滑块轨道、可拖动手柄及数值标记
  • 维护状态变量记录当前滑块位置,实时更新UI

代码示例

import 'package:flutter/material.dart';

class CustomSlider extends StatefulWidget {
  final double min;
  final double max;
  final ValueChanged<double>? onChanged;

  const CustomSlider({
    super.key,
    this.min = 0,
    this.max = 100,
    this.onChanged,
  });

  @override
  State<CustomSlider> createState() => _CustomSliderState();
}

class _CustomSliderState extends State<CustomSlider> {
  double _currentValue = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onHorizontalDragUpdate: (details) {
        final box = context.findRenderObject() as RenderBox;
        final offsetX = details.localPosition.dx;
        final width = box.size.width;
        // 计算当前值,限制在min和max区间内
        final value = widget.min + (widget.max - widget.min) * (offsetX / width);
        setState(() {
          _currentValue = value.clamp(widget.min, widget.max);
        });
        widget.onChanged?.call(_currentValue);
      },
      child: CustomPaint(
        size: const Size(double.infinity, 60),
        painter: _SliderPainter(currentValue: _currentValue, min: widget.min, max: widget.max),
      ),
    );
  }
}

class _SliderPainter extends CustomPainter {
  final double currentValue;
  final double min;
  final double max;

  _SliderPainter({required this.currentValue, required this.min, required this.max});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade300
      ..strokeWidth = 4
      ..style = PaintingStyle.stroke;

    // 绘制基础轨道
    final trackRect = Rect.fromLTWH(20, size.height / 2 - 2, size.width - 40, 4);
    canvas.drawRRect(RRect.fromRectAndRadius(trackRect, const Radius.circular(2)), paint);

    // 绘制已选中轨道段
    final progress = (currentValue - min) / (max - min);
    final progressWidth = (size.width - 40) * progress;
    final progressRect = Rect.fromLTWH(20, size.height / 2 - 2, progressWidth, 4);
    paint.color = Colors.blue;
    canvas.drawRRect(RRect.fromRectAndRadius(progressRect, const Radius.circular(2)), paint);

    // 绘制滑块手柄
    final handleX = 20 + progressWidth;
    paint.color = Colors.white;
    paint.style = PaintingStyle.fill;
    canvas.drawCircle(Offset(handleX, size.height / 2), 12, paint);
    paint.color = Colors.blue;
    paint.style = PaintingStyle.stroke;
    paint.strokeWidth = 2;
    canvas.drawCircle(Offset(handleX, size.height / 2), 12, paint);

    // 绘制当前数值标记
    final textPainter = TextPainter(
      text: TextSpan(
        text: '${currentValue.toStringAsFixed(1)}',
        style: const TextStyle(color: Colors.black, fontSize: 16),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(canvas, Offset(handleX - textPainter.width / 2, size.height / 2 + 16));
  }

  @override
  bool shouldRepaint(covariant _SliderPainter oldDelegate) {
    return oldDelegate.currentValue != currentValue;
  }
}

// 使用示例
void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(
        child: Padding(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: CustomSlider(min: 0, max: 100),
        ),
      ),
    ),
  ));
}

扩展优化方向

  • 增加刻度线、自定义颜色样式的可配置参数
  • 支持垂直方向滑块的切换
  • 添加拖动结束回调,减少频繁状态更新的性能消耗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 09:20:30