You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现轨道厚度渐变递增的自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:05:58