Flutter中如何为Slider轨道的divisions分段区间分别设置独立颜色
Flutter 实现分段着色自定义Slider
核心实现思路
Flutter原生Slider组件的轨道默认是统一填充色,要实现每个division分段独立着色的效果,核心是自定义Slider轨道形状,重写分段绘制逻辑,不需要引入第三方插件即可完成。
实现步骤
- 编写可复用的分段轨道绘制类
继承Flutter内置的SliderTrackShape基类,重写两个核心方法:getPreferredRect:复用系统默认的轨道尺寸计算规则,保证轨道的高度、上下边距和原生Slider完全对齐,不会出现布局错位paint:核心绘制逻辑,先根据轨道的总宽度计算每一段的宽度,逐段使用预设的颜色列表绘制圆角轨道块,最后叠加未选中区域的半透明暗化遮罩,模拟原生Slider的交互反馈
- 挂载自定义轨道到SliderTheme
将自定义的轨道形状赋值给SliderThemeData的trackShape参数,同时把原生默认的激活/非激活轨道颜色设为透明,避免和自定义绘制的彩色轨道重叠 - 配置Slider组件的基础参数
将Slider的divisions数值设置为和颜色列表长度一致,绑定值更新回调即可完成效果
完整代码示例
自定义轨道类
import 'package:flutter/material.dart'; class SegmentedTrackShape extends SliderTrackShape { // 每个分段对应的颜色列表 final List<Color> segmentColors; const SegmentedTrackShape({required this.segmentColors}); @override Rect getPreferredRect({ required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = false, bool isDiscrete = false, }) { // 复用默认逻辑计算轨道的边界尺寸 final double trackHeight = sliderTheme.trackHeight!; final double trackLeft = offset.dx; final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2; final double trackWidth = parentBox.size.width; return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); } @override void paint( PaintingContext context, Offset offset, { required RenderBox parentBox, required SliderThemeData sliderTheme, required Animation<double> enableAnimation, required Offset thumbCenter, Offset? secondaryOffset, bool isEnabled = false, bool isDiscrete = false, required TextDirection textDirection, }) { final Paint segmentPaint = Paint(); final Rect trackRect = getPreferredRect( parentBox: parentBox, offset: offset, sliderTheme: sliderTheme, isEnabled: isEnabled, isDiscrete: isDiscrete, ); // 计算单段轨道的宽度,保证分段数和颜色列表长度对齐 final double segmentWidth = trackRect.width / segmentColors.length; final Radius trackRadius = Radius.circular(trackRect.height / 2); // 从左到右逐段绘制彩色轨道块 for (int i = 0; i < segmentColors.length; i++) { segmentPaint.color = segmentColors[i]; final Rect segmentRect = Rect.fromLTWH( trackRect.left + i * segmentWidth, trackRect.top, segmentWidth, trackRect.height, ); context.canvas.drawRRect( RRect.fromRectAndRadius(segmentRect, trackRadius), segmentPaint, ); } // 绘制滑块右侧未选中区域的暗化遮罩,模拟原生交互反馈 final Paint inactiveMaskPaint = Paint()..color = Colors.black12; final Rect inactiveAreaRect = Rect.fromLTRB( thumbCenter.dx, trackRect.top, trackRect.right, trackRect.bottom, ); context.canvas.drawRRect( RRect.fromRectAndRadius(inactiveAreaRect, trackRadius), inactiveMaskPaint, ); } }
组件调用示例
// 当前滑块值 double currentValue = 0.3; // 预设的分段颜色列表 final List<Color> _divisionColors = [ Color(0xff8DF5DE), Color(0xffA8F58C), Color(0xffFCEC74), Color(0xffFCD376), Color(0xffFDB272), Color(0xffE27E5A), ]; SliderTheme( data: SliderThemeData( trackHeight: 8, // 轨道高度 trackShape: SegmentedTrackShape(segmentColors: _divisionColors), // 隐藏原生默认轨道,避免和自定义绘制的轨道重叠 activeTrackColor: Colors.transparent, inactiveTrackColor: Colors.transparent, thumbColor: Colors.white, thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 12), ), child: Slider( value: currentValue, min: 0, max: 1, divisions: _divisionColors.length, // 分段数和颜色列表长度匹配 onChanged: (value) { // 绑定值更新逻辑 currentValue = value; }, ), )
注意事项
- 必须保证Slider的
divisions值和传入自定义轨道的颜色列表长度完全匹配,否则会出现分段数和颜色块数量错位的问题 - 可以根据设计需求调整
trackHeight(轨道高度)、enabledThumbRadius(滑块大小)、未选中遮罩的透明度,适配不同的视觉风格 - 如果应用需要支持从右到左的RTL语言显示,需要在绘制逻辑中根据
textDirection参数调整分段的绘制顺序,避免颜色排列方向颠倒
内容的提问来源于stack exchange,提问作者Eduards
相关产品推荐
相关产品推荐

