基于Flutter实现与参考图一致的自定义Slider组件需求
自定义Flutter Slider实现匹配设计样式
原生Flutter Slider的样式定制能力有限,要实现你需要的特殊样式,我们可以通过自定义SliderTheme结合自定义滑块组件来完成,或者更灵活地使用CustomPaint绘制完全自定义的轨道和滑块。
方案一:基于SliderTheme的定制(适合大部分场景)
通过修改SliderThemeData来调整轨道、滑块、刻度等样式,以下是匹配参考样式的示例代码:
class CustomStyledSlider extends StatelessWidget { final double value; final ValueChanged<double> onChanged; const CustomStyledSlider({ super.key, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { return SliderTheme( data: SliderTheme.of(context).copyWith( // 激活状态轨道渐变样式 activeTrackColor: const LinearGradient( colors: [Colors.blueAccent, Colors.purpleAccent], begin: Alignment.centerLeft, end: Alignment.centerRight, ).createShader(const Rect.fromLTWH(0, 0, 300, 4)), // 未激活状态轨道颜色 inactiveTrackColor: Colors.grey.shade300, // 轨道高度 trackHeight: 6, // 自定义滑块形状 thumbShape: CustomSliderThumbShape(), // 轨道两端圆角 trackShape: const RoundedRectSliderTrackShape( trackRadius: 3, ), ), child: Slider( value: value, min: 0, max: 100, onChanged: onChanged, ), ); } } // 自定义滑块形状 class CustomSliderThumbShape extends SliderComponentShape { @override Size getPreferredSize(bool isEnabled, bool isDiscrete) { return const Size(24, 24); } @override void paint( PaintingContext context, Offset center, { required Animation<double> activationAnimation, required Animation<double> enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow, }) { final canvas = context.canvas; // 绘制滑块阴影 canvas.drawCircle( center, 12, Paint() ..color = Colors.black.withOpacity(0.1) ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4), ); // 绘制滑块外层白色圆 canvas.drawCircle( center, 10, Paint()..color = Colors.white, ); // 绘制滑块内层蓝色指示器 canvas.drawCircle( center, 6, Paint()..color = Colors.blueAccent, ); } }
方案二:完全自定义绘制(适合极致定制需求)
如果参考样式有更特殊的轨道或交互逻辑,可以用CustomPaint从头绘制:
class FullyCustomSlider extends StatefulWidget { final double value; final ValueChanged<double> onChanged; const FullyCustomSlider({ super.key, required this.value, required this.onChanged, }); @override State<FullyCustomSlider> createState() => _FullyCustomSliderState(); } class _FullyCustomSliderState extends State<FullyCustomSlider> { late double _currentValue; @override void initState() { super.initState(); _currentValue = widget.value; } @override Widget build(BuildContext context) { return GestureDetector( onHorizontalDragUpdate: (details) { final box = context.findRenderObject() as RenderBox; final localPos = box.globalToLocal(details.globalPosition); final trackWidth = box.size.width - 24; // 减去滑块宽度 final newValue = (localPos.dx - 12) / trackWidth; final clampedValue = newValue.clamp(0.0, 1.0) * 100; setState(() => _currentValue = clampedValue); widget.onChanged(clampedValue); }, onTapDown: (details) { final box = context.findRenderObject() as RenderBox; final localPos = box.globalToLocal(details.globalPosition); final trackWidth = box.size.width - 24; final newValue = (localPos.dx - 12) / trackWidth; final clampedValue = newValue.clamp(0.0, 1.0) * 100; setState(() => _currentValue = clampedValue); widget.onChanged(clampedValue); }, child: CustomPaint( size: const Size(double.infinity, 40), painter: _CustomSliderPainter(value: _currentValue), ), ); } } class _CustomSliderPainter extends CustomPainter { final double value; _CustomSliderPainter({required this.value}); @override void paint(Canvas canvas, Size size) { final trackHeight = 6.0; final trackY = size.height / 2 - trackHeight / 2; final trackWidth = size.width - 24; final activeWidth = trackWidth * (value / 100); // 绘制未激活轨道 canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(12, trackY, trackWidth, trackHeight), const Radius.circular(3), ), Paint()..color = Colors.grey.shade300, ); // 绘制激活渐变轨道 final gradient = LinearGradient( colors: [Colors.blueAccent, Colors.purpleAccent], begin: Alignment.centerLeft, end: Alignment.centerRight, ); canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(12, trackY, activeWidth, trackHeight), const Radius.circular(3), ), Paint()..shader = gradient.createShader(Rect.fromLTWH(12, trackY, activeWidth, trackHeight)), ); // 绘制滑块 final sliderX = 12 + activeWidth; final sliderY = size.height / 2; // 滑块阴影 canvas.drawCircle( Offset(sliderX, sliderY), 12, Paint() ..color = Colors.black.withOpacity(0.1) ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4), ); // 滑块外层 canvas.drawCircle( Offset(sliderX, sliderY), 10, Paint()..color = Colors.white, ); // 滑块内层指示器 canvas.drawCircle( Offset(sliderX, sliderY), 6, Paint()..color = Colors.blueAccent, ); } @override bool shouldRepaint(covariant _CustomSliderPainter oldDelegate) { return oldDelegate.value != value; } }
使用时直接调用组件即可:
CustomStyledSlider( value: _sliderValue, onChanged: (val) => setState(() => _sliderValue = val), )
你可以根据参考图的具体细节调整颜色、尺寸、渐变方向等参数,比如修改滑块的形状、轨道的圆角大小或者添加刻度标记。
内容的提问来源于stack exchange,提问作者Ahsan Ilyas
相关产品推荐
相关产品推荐

