如何在滑块上方添加刻度和标签?

2024-04-26

我尝试使用 Slider().. 划分看起来不错(值 >= 50 ? 10 : 20)

但是,如何在滑块上方添加刻度和标签?

Expect :

勾选将根据滑块位置改变颜色

Actual:

Slider(
 min: 0,
 max: 100,
 value: value,
 onChanged: (val) {
   setState(() {
     value = val;
   });
  },
 divisions: value >= 50 ? 10 : 20,
 label: value.toString(),
),

我的问题是:

  1. 标签和刻度的位置(如果使用列)
  2. 如果滑块位置相同,则更改刻度颜色

我的代码使用 Column(

Column(
      children: [
        Container(
          margin: EdgeInsets.symmetric(horizontal: 20),
          child:
          Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: List.generate(6, (index) => Text('$index')),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: List.generate(
                  16,
                  (index) => SizedBox(
                    height: 8,
                    child: VerticalDivider(
                      width: 8,
                      color: HelperColors.orange,
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
        Slider(
          value: widget.value,
          min: widget.minValue,
          max: widget.maxValue,
          divisions: widget.divisions,
          onChanged: widget.onChanged,
          label: widget.value.toString(),
        ),
      ],
    );

你能帮我解决这个设计上的一些问题吗?


刻度和刻度值布局

与其将刻度线和刻度值行包装在一列中,不如将刻度线和刻度值组合为单个列可能更好。然后,将此列包裹起来Expanded小部件并将其放入List.generate一行。这保证了刻度始终与刻度值对齐,并且每列具有相等的间距。

刻度线和滑块对齐

默认情况下,滑块带有偏移量,以便为拇指和覆盖层腾出空间。即使您精确计算了屏幕上刻度之间的间距,但在不同屏幕尺寸上进行测试时,间距仍然会拉伸。

如果您有兴趣了解有关滑块偏移/边距的更多信息,请查看此链接。
滑块偏移问题 https://github.com/flutter/flutter/issues/37057

为了实现完美对齐,您需要执行以下操作

  1. 通过创建自定义来删除偏移量trackShape的滑块。

  2. 测量一刻度间距的偏移量并将其除以 2。
    公式为MediaQuery.of(context).size.width / numOfTick / 2

  3. 将零偏移滑块包裹起来Paddingwidget 并使用计算出的偏移量作为水平填充值。padding: EdgeInsets.symmetric(horizontal: offset),

    offset
      V
    |---|
    .----------/   /------------.
    |   0   |           |  100  |    
    |       |Ticks area |       |
    |   |   |           |   |   ||
    .----------/   /------------.|
    |                           ||
    |       Slider area         ||Screen edge
    |                           ||
    '----------/  /-------------'|
    
  4. 现在,无论屏幕尺寸如何,刻度线和滑块将始终从边到边完美对齐。

这是例子。您可能仍想根据您的要求改进 UI。

支持什么

  1. 可调整的主要和次要刻度
  2. 当值匹配时勾选突出显示
  3. 数值精度控制
double value = 50;
double actualValue = 50;
double minValue = 0;
double maxValue = 100;
List<double> steps = [0,5,10,15,20,25,30,35,40,45,50,60,70,80,90,100];

// ...

CustomSlider(
            minValue: minValue,
            maxValue: maxValue,
            value: value,
            majorTick: 6,
            minorTick: 2,
            labelValuePrecision: 0,
            tickValuePrecision: 0,
            onChanged: (val) => setState(() {
              value = val;
              actualValue =
                  steps[(val / maxValue * (steps.length - 1)).ceil().toInt()];
              print('Slider value (linear): $value');
              print('Actual value (non-linear): $actualValue');
            }),
            activeColor: Colors.orange,
            inactiveColor: Colors.orange.shade50,
            linearStep: false,
            steps: steps,
          ),

自定义滑块小部件

class CustomSlider extends StatelessWidget {
  final double value;
  final double minValue;
  final double maxValue;
  final int majorTick;
  final int minorTick;
  final Function(double)? onChanged;
  final Color? activeColor;
  final Color? inactiveColor;
  final int labelValuePrecision;
  final int tickValuePrecision;
  final bool linearStep;
  final List<double>? steps;

  CustomSlider({
    required this.value,
    required this.minValue,
    required this.maxValue,
    required this.majorTick,
    required this.minorTick,
    required this.onChanged,
    this.activeColor,
    this.inactiveColor,
    this.labelValuePrecision = 2,
    this.tickValuePrecision = 1,
    this.linearStep = true,
    this.steps,
  });

  @override
  Widget build(BuildContext context) {
    final allocatedHeight = MediaQuery.of(context).size.height;
    final allocatedWidth = MediaQuery.of(context).size.width;
    final divisions = (majorTick - 1) * minorTick + majorTick;
    final double valueHeight =
        allocatedHeight * 0.05 < 41 ? 41 : allocatedHeight * 0.05;
    final double tickHeight =
        allocatedHeight * 0.025 < 20 ? 20 : allocatedHeight * 0.025;
    final labelOffset = allocatedWidth / divisions / 2;

    return Column(
      children: [
        Row(
          children: List.generate(
            divisions,
            (index) => Expanded(
              child: Column(
                children: [
                  Container(
                    alignment: Alignment.bottomCenter,
                    height: valueHeight,
                    child: index % (minorTick + 1) == 0
                        ? Text(
                            linearStep
                                ? '${(index / (divisions - 1) * maxValue).toStringAsFixed(tickValuePrecision)}'
                                : '${(steps?[index])?.toStringAsFixed(tickValuePrecision)}',
                            style: TextStyle(
                              fontSize: 12,
                            ),
                            textAlign: TextAlign.center,
                          )
                        : null,
                  ),
                  Container(
                    alignment: Alignment.bottomCenter,
                    height: tickHeight,
                    child: VerticalDivider(
                      indent: index % (minorTick + 1) == 0 ? 2 : 6,
                      thickness: 1.2,
                      color: (index / (divisions - 1)) * maxValue == value
                          ? activeColor ?? Colors.orange
                          : Colors.grey.shade300,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
        Padding(
          padding: EdgeInsets.symmetric(horizontal: labelOffset),
          child: SliderTheme(
            data: SliderThemeData(
              trackHeight:
                  allocatedHeight * 0.011 < 9 ? 9 : allocatedHeight * 0.011,
              activeTickMarkColor: activeColor ?? Colors.orange,
              inactiveTickMarkColor: inactiveColor ?? Colors.orange.shade50,
              activeTrackColor: activeColor ?? Colors.orange,
              inactiveTrackColor: inactiveColor ?? Colors.orange.shade50,
              thumbColor: activeColor ?? Colors.orange,
              overlayColor: activeColor == null
                  ? Colors.orange.withOpacity(0.1)
                  : activeColor!.withOpacity(0.1),
              thumbShape: RoundSliderThumbShape(enabledThumbRadius: 12.0),
              trackShape: CustomTrackShape(),
              showValueIndicator: ShowValueIndicator.never,
              valueIndicatorTextStyle: TextStyle(
                fontSize: 12,
              ),
            ),
            child: Slider(
              value: value,
              min: minValue,
              max: maxValue,
              divisions: divisions - 1,
              onChanged: onChanged,
              label: value.toStringAsFixed(labelValuePrecision),
            ),
          ),
        ),
      ],
    );
  }
}

class CustomTrackShape extends RoundedRectSliderTrackShape {
  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);
  }
}

Test

编辑(具有非线性步骤的滑块)

由于 Flutter 滑块具有线性值-位置映射,因此基于非线性值更改滑块值可能不是一个好主意。但是,仍然可以通过为实际值创建附加值范围然后将它们映射在一起来实现这一点。

以下是步骤

  1. 创建一个包含非线性值的列表(您创建的列表)
  2. 确保列表的长度与刻度总数匹配。否则,映射将不正确。
    listLength = (majorTick - 1) * minorTick + majorTick

已对上述代码进行编辑以解决此问题。

Test

映射值

                           Start                            End
Slider value (linear)      0.00 6.67 13.33 ... 86.67 93.33 100.0
Actual value (non-linear)  0.00 5.00 10.00 ... 80.00 90.00 100.0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在滑块上方添加刻度和标签? 的相关文章