一个用来实现胶囊滑条的代码 使用方法
import 'package:flutter/material.dart';
import 'package:permission_handler_test/capsule_silder/capsule_slider.dart';
void main() {
runApp(const MaterialApp(
home: CapsuleSliderTest()));
}
class CapsuleSliderTest extends StatefulWidget {
const CapsuleSliderTest({super.key});
@override
State<CapsuleSliderTest> createState() => _CapsuleSliderTestState();
}
class _CapsuleSliderTestState extends State<CapsuleSliderTest> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
children: [
const SizedBox(height: 100,),
CapsuleSlider(value: 1, min: 0, max: 100,
onChanged: (v){
},)
],
),
),
);
}
}
详细注释在代码中 若有其他问题可以评论区或私信
import 'dart:math' as math;
import 'package:flutter/material.dart';
/// 胶囊形拖动条(纯手势实现,不依赖 Slider)
/// ---------------------------------------------------------------------------
/// * value 当前数值
/// * min / max 取值范围
/// * onChanged 拖动过程中实时回调
/// * onChangeEnd 抬手回调
/// * move 是否处于“可调节”状态,影响轨道颜色 也可以放在禁用手势,此处只是用作颜色区分
/// * height 轨道高度(= 圆角直径)
/// * activeColor move==true 时已选轨道颜色
/// * noMoveColor move==false 时已选轨道颜色
/// * inactiveColor 未选轨道颜色
/// * showThumb 是否显示小圆手柄
/// ---------------------------------------------------------------------------
class CapsuleSlider extends StatefulWidget {
const CapsuleSlider({
super.key,
required this.value,
required this.min,
required this.max,
required this.onChanged,
this.move = false,
this.onChangeEnd,
this.height = 46,
this.activeColor = const Color(0xfffa0200),
this.inactiveColor = const Color(0xffF3F4F9),
this.noMoveColor = const Color(0xffD6D6D6),
this.showThumb = false,
this.thumbRadius = 10,
this.thumbColor = Colors.white,
});
// ---- 对外属性 ----
final double value;
final double min;
final double max;
final bool move;
final ValueChanged<double> onChanged;
final ValueChanged<double>? onChangeEnd;
final double height; // 轨道高度
final Color activeColor; // 可调节时的已选颜色
final Color inactiveColor; // 背景颜色
final Color noMoveColor; // 不可调节时的已选颜色
final bool showThumb;
final double thumbRadius;
final Color thumbColor;
@override
State<CapsuleSlider> createState() => _CapsuleSliderState();
}
class _CapsuleSliderState extends State<CapsuleSlider> {
late double _value; // 内部持有当前值,实时刷新
late double _width; // 组件宽度,用于像素↔百分比换算
@override
void initState() {
super.initState();
_value = widget.value; // 初始化
}
/// 当父组件外部更新 value 时,同步内部 _value
@override
void didUpdateWidget(covariant CapsuleSlider old) {
super.didUpdateWidget(old);
if (old.value != widget.value) _value = widget.value;
}
/// 处理一次拖动 / 点击,localPos 为手指在组件内的坐标
void _handleDrag(Offset localPos) {
// 把 X 坐标映射到 0~1 百分比
final pct = (localPos.dx / _width).clamp(0.0, 1.0);
// 再映射到实际 min~max 区间
final newVal = widget.min + pct * (widget.max - widget.min);
if (newVal != _value) {
setState(() => _value = newVal); // 刷新 UI
widget.onChanged(newVal); // 通知外部
}
}
@override
Widget build(BuildContext context) {
final r = widget.height / 2; // 圆角半径
final pct = (_value - widget.min) / (widget.max - widget.min);
return LayoutBuilder(
builder: (_, constraints) {
_width = constraints.maxWidth; // 得到真实宽度
final activeW = _width * pct; // 已选轨道宽度
return GestureDetector(
behavior: HitTestBehavior.translucent, // 空白处也能响应
// —— 手势回调:点击 / 拖动 / 抬手 ——
onPanDown: (d) => _handleDrag(d.localPosition),
onPanUpdate:(d) => _handleDrag(d.localPosition),
onPanEnd: (_) => widget.onChangeEnd?.call(_value),
onTapDown: (d) => _handleDrag(d.localPosition),
// —— 裁剪为胶囊形,防止已选轨道溢出 ——
child: ClipRRect(
borderRadius: BorderRadius.circular(r),
child: Stack(
alignment: Alignment.centerLeft,
children: [
// 背景轨道(未选部分)
Container(
height: widget.height,
decoration: BoxDecoration(
color: widget.inactiveColor,
borderRadius: BorderRadius.circular(r),
),
),
// 已选轨道:颜色取决于 move
Container(
height: widget.height,
width: activeW,
decoration: BoxDecoration(
color: widget.move
? widget.activeColor
: widget.noMoveColor,
borderRadius: BorderRadius.circular(r),
),
),
// 可选:手柄
if (widget.showThumb)
Positioned(
// 让手柄中心对齐已选轨道右端
left: math.max(0, activeW - widget.thumbRadius),
child: Container(
width: widget.thumbRadius * 2,
height: widget.thumbRadius * 2,
decoration: BoxDecoration(
color: widget.thumbColor,
shape: BoxShape.circle,
boxShadow: [
// 简单投影,让手柄有悬浮感
BoxShadow(
color: Colors.black26,
blurRadius: 3,
offset: const Offset(0, 1),
)
],
),
),
),
],
),
),
);
},
);
}
}