Flutter如何通过Triangle Slider Track Shape实现Windows音量滑块
Flutter实现Windows控制面板风格音量滑块

基于自定义Slider轨道形状实现,无需引入第三方依赖,直接编写代码即可完成效果。
核心实现逻辑
Windows音量滑块的视觉特征非常明确:
- 轨道高度4px,滑块左侧为系统蓝色激活态,右侧为浅灰色未激活态
- 滑块手柄为竖置白色圆角矩形,带1px浅灰色描边
- 拖拽时无多余水波纹效果,交互反馈干净
如果要基于TriangleSliderTrackShape改造,只需要替换原类中三角路径的绘制逻辑,改为绘制平直的圆角矩形轨道即可,完整可运行代码如下:
1. 自定义轨道形状
import 'package:flutter/material.dart'; class WindowsVolumeTrackShape extends SliderTrackShape { const WindowsVolumeTrackShape(); @override Rect getPreferredRect({ required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = false, bool isDiscrete = false, }) { final trackHeight = sliderTheme.trackHeight ?? 4.0; final trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2; return Rect.fromLTWH(offset.dx, trackTop, parentBox.size.width, trackHeight); } @override void paint( PaintingContext context, Offset offset, { required RenderBox parentBox, required SliderThemeData sliderTheme, required Animation<double> enableAnimation, required TextDirection textDirection, required Offset thumbCenter, Offset? secondaryOffset, bool isDiscrete = false, bool isEnabled = false, }) { final canvas = context.canvas; final trackRect = getPreferredRect( parentBox: parentBox, offset: offset, sliderTheme: sliderTheme, isEnabled: isEnabled, isDiscrete: isDiscrete, ); // 绘制未激活灰色轨道 canvas.drawRRect( RRect.fromRectAndRadius(trackRect, const Radius.circular(0.5)), Paint()..color = const Color(0xFFE6E6E6), ); // 绘制激活蓝色轨道 final activeRect = Rect.fromLTRB( trackRect.left, trackRect.top, thumbCenter.dx, trackRect.bottom, ); canvas.drawRRect( RRect.fromRectAndRadius(activeRect, const Radius.circular(0.5)), Paint()..color = const Color(0xFF0078D7), ); } }
2. 自定义滑块手柄形状
class WindowsVolumeThumbShape extends SliderComponentShape { const WindowsVolumeThumbShape({ this.width = 8, this.height = 20, this.radius = 2, }); final double width; final double height; final double radius; @override Size getPreferredSize(bool isEnabled, bool isDiscrete) => Size(width, height); @override void paint( PaintingContext context, Offset center, { required Animation<double> enableAnimation, required TextDirection textDirection, required SliderThemeData sliderTheme, bool isDiscrete = false, bool isEnabled = false, }) { final canvas = context.canvas; final thumbRect = Rect.fromCenter(center: center, width: width, height: height); final rRect = RRect.fromRectAndRadius(thumbRect, Radius.circular(radius)); // 白色填充 canvas.drawRRect(rRect, Paint()..color = Colors.white); // 灰色描边 canvas.drawRRect( rRect, Paint() ..color = const Color(0xFFCCCCCC) ..style = PaintingStyle.stroke ..strokeWidth = 1, ); } }
3. 组件集成使用
class VolumeSlider extends StatefulWidget { const VolumeSlider({super.key, required this.onVolumeChange}); final ValueChanged<double> onVolumeChange; @override State<VolumeSlider> createState() => _VolumeSliderState(); } class _VolumeSliderState extends State<VolumeSlider> { double _currentVolume = 0.65; @override Widget build(BuildContext context) { return SliderTheme( data: SliderThemeData( trackHeight: 4, trackShape: const WindowsVolumeTrackShape(), thumbShape: const WindowsVolumeThumbShape(), overlayShape: SliderComponentShape.noOverlay, ), child: Slider( value: _currentVolume, onChanged: (val) { setState(() => _currentVolume = val); widget.onVolumeChange(val); }, ), ); } }
参数调整说明
- 如果需要基于现有
TriangleSliderTrackShape修改,直接把原类paint方法中绘制三角形路径的代码删除,替换为上面的圆角矩形轨道绘制逻辑即可 - 色值、尺寸参数完全对齐Windows10/11原生音量控件,可根据实际显示需求微调
- 不需要的水波纹、刻度高亮效果可以通过
SliderThemeData的overlay、tick等属性关闭
内容的提问来源于stack exchange,提问作者Prabhakaran
相关产品推荐
相关产品推荐

