如何实现轨道厚度渐变递增的自定义Flutter Slider
解决方案
仅通过重写getPreferredRect无法实现轨道厚度渐变,因为这个方法仅定义轨道的占位矩形。要实现从左到右厚度递增的滑块轨道,需要重写RoundedRectSliderTrackShape的paint方法,自定义绘制逻辑。
自定义轨道形状实现
替换你原来的CustomSliderTrackShape为以下代码:
class CustomSliderTrackShape extends RoundedRectSliderTrackShape { final double minTrackHeight; final double maxTrackHeight; const CustomSliderTrackShape({ this.minTrackHeight = 2.0, this.maxTrackHeight = 12.0, }); @override void paint( PaintingContext context, Offset offset, { required RenderBox parentBox, required SliderThemeData sliderTheme, required Animation<double> enableAnimation, required TextDirection textDirection, required Offset thumbCenter, bool isDiscrete = false, bool isEnabled = false, double additionalActiveTrackHeight = 2, }) { // 计算轨道可用宽度(减去左右滑块的半径) final double thumbRadius = sliderTheme.thumbShape!.getPreferredSize(false, false).width / 2; final double trackTotalWidth = parentBox.size.width - 2 * thumbRadius; final double trackLeft = offset.dx + thumbRadius; final double trackVerticalCenter = offset.dy + parentBox.size.height / 2; // 已激活轨道的宽度(从起点到滑块中心) final double activeTrackWidth = thumbCenter.dx - trackLeft; // 滑块位置对应的轨道厚度 final double currentTrackHeight = minTrackHeight + (maxTrackHeight - minTrackHeight) * (activeTrackWidth / trackTotalWidth); // 绘制未激活轨道(滑块右侧) final Paint inactivePaint = Paint() ..color = sliderTheme.inactiveTrackColor! ..style = PaintingStyle.fill; final Path inactivePath = Path(); inactivePath.moveTo(thumbCenter.dx, trackVerticalCenter - currentTrackHeight / 2); inactivePath.lineTo(parentBox.size.width - thumbRadius, trackVerticalCenter - maxTrackHeight / 2); inactivePath.lineTo(parentBox.size.width - thumbRadius, trackVerticalCenter + maxTrackHeight / 2); inactivePath.lineTo(thumbCenter.dx, trackVerticalCenter + currentTrackHeight / 2); inactivePath.close(); context.canvas.drawPath(inactivePath, inactivePaint); // 绘制已激活轨道(滑块左侧) final Paint activePaint = Paint() ..color = sliderTheme.activeTrackColor! ..style = PaintingStyle.fill; final Path activePath = Path(); activePath.moveTo(trackLeft, trackVerticalCenter - minTrackHeight / 2); activePath.lineTo(thumbCenter.dx, trackVerticalCenter - currentTrackHeight / 2); activePath.lineTo(thumbCenter.dx, trackVerticalCenter + currentTrackHeight / 2); activePath.lineTo(trackLeft, trackVerticalCenter + minTrackHeight / 2); activePath.close(); context.canvas.drawPath(activePath, activePaint); } }
使用方式
在SliderTheme中配置自定义轨道形状,可通过minTrackHeight和maxTrackHeight调整渐变范围:
SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: Colors.blue, inactiveTrackColor: Colors.grey.shade300, // 配置自定义轨道 trackShape: const CustomSliderTrackShape( minTrackHeight: 2.0, maxTrackHeight: 12.0, ), // 调整滑块样式匹配轨道厚度 thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 12.0), ), child: Slider( min: 0, max: 100, value: _value, onChanged: (value) => setState(() => _value = value), ), )
完整可运行代码
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('自定义渐变厚度滑块')), body: const Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: MySlider(), ), ), ); } } class MySlider extends StatefulWidget { const MySlider({super.key}); @override _MySliderState createState() => _MySliderState(); } class _MySliderState extends State<MySlider> { double _value = 50; @override Widget build(BuildContext context) { return SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: Colors.blue, inactiveTrackColor: Colors.grey.shade300, trackShape: const CustomSliderTrackShape( minTrackHeight: 2.0, maxTrackHeight: 12.0, ), thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 12.0), ), child: Slider( min: 0, max: 100, divisions: 100, value: _value, onChanged: (value) { setState(() { _value = value; }); }, ), ); } } class CustomSliderTrackShape extends RoundedRectSliderTrackShape { final double minTrackHeight; final double maxTrackHeight; const CustomSliderTrackShape({ this.minTrackHeight = 2.0, this.maxTrackHeight = 12.0, }); @override void paint( PaintingContext context, Offset offset, { required RenderBox parentBox, required SliderThemeData sliderTheme, required Animation<double> enableAnimation, required TextDirection textDirection, required Offset thumbCenter, bool isDiscrete = false, bool isEnabled = false, double additionalActiveTrackHeight = 2, }) { final double thumbRadius = sliderTheme.thumbShape!.getPreferredSize(false, false).width / 2; final double trackTotalWidth = parentBox.size.width - 2 * thumbRadius; final double trackLeft = offset.dx + thumbRadius; final double trackVerticalCenter = offset.dy + parentBox.size.height / 2; final double activeTrackWidth = thumbCenter.dx - trackLeft; final double currentTrackHeight = minTrackHeight + (maxTrackHeight - minTrackHeight) * (activeTrackWidth / trackTotalWidth); // 绘制未激活轨道 final Paint inactivePaint = Paint() ..color = sliderTheme.inactiveTrackColor! ..style = PaintingStyle.fill; final Path inactivePath = Path(); inactivePath.moveTo(thumbCenter.dx, trackVerticalCenter - currentTrackHeight / 2); inactivePath.lineTo(parentBox.size.width - thumbRadius, trackVerticalCenter - maxTrackHeight / 2); inactivePath.lineTo(parentBox.size.width - thumbRadius, trackVerticalCenter + maxTrackHeight / 2); inactivePath.lineTo(thumbCenter.dx, trackVerticalCenter + currentTrackHeight / 2); inactivePath.close(); context.canvas.drawPath(inactivePath, inactivePaint); // 绘制已激活轨道 final Paint activePaint = Paint() ..color = sliderTheme.activeTrackColor! ..style = PaintingStyle.fill; final Path activePath = Path(); activePath.moveTo(trackLeft, trackVerticalCenter - minTrackHeight / 2); activePath.lineTo(thumbCenter.dx, trackVerticalCenter - currentTrackHeight / 2); activePath.lineTo(thumbCenter.dx, trackVerticalCenter + currentTrackHeight / 2); activePath.lineTo(trackLeft, trackVerticalCenter + minTrackHeight / 2); activePath.close(); context.canvas.drawPath(activePath, activePaint); } }
内容的提问来源于stack exchange,提问作者MendelG
相关产品推荐
相关产品推荐

