Flutter RangeSlider — метки всегда отображаются над панелью дорожек

#flutter #slider #flutter-layout

#flutter #ползунок #flutter-макет

Вопрос:

Я использую RangeSlider виджет, вместо отображения выбранных значений при скольжении я хотел бы, чтобы значения моих основных подразделений отображались над панелью треков. В настоящее время единственное решение, которое я придумал, — это поместить несколько Row Text виджетов над моим слайдером, но значения и метки не выровнены должным образом.

Мой код

 final _sliderLabels = <String>[
    'Bac',
    'Bac  2',
    'Bac  3',
    'Bac  4',
    'Bac  5',
    '>Bac  5'
  ];

Widget _buildSlider() {
    return Column(
      children: [
        _buildLabel("Niveaux d'études"),
        SizedBox(height: 10),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ..._sliderLabels.map((e) => Text(e)),
          ],
        ),
        SliderTheme(
          data: SliderThemeData(
            showValueIndicator: ShowValueIndicator.never,
            thumbColor: Colors.blue,
            trackHeight: 2,
            activeTrackColor: Colors.blue,
            inactiveTrackColor: Colors.grey,
          ),
          child: StreamBuilder<RangeValues>(
            stream: _onSliderValuesChanged,
            builder: (_, snapshot) {
              if (!snapshot.hasData) return Container();
              return RangeSlider(
                onChanged: (value) => _sliderValuesController.sink.add(value),
                min: 0,
                max: _sliderLabels.length.toDouble(),
                divisions: _sliderLabels.length,
                values: snapshot.data,
              );
            },
          ),
        ),
      ],
    );
  }
  

Изображение
введите описание изображения здесь

Как вы можете видеть, метка Bac 5 не выровнена с моим большим пальцем.

У вас есть какие-либо предложения?

Ответ №1:

Для тех, кто ищет способ всегда показывать метки.Я создал (на мой взгляд) общий виджет слайдера диапазона, когда пытался сделать то же самое. Для всех входных данных есть значения по умолчанию, поэтому они будут «работать из коробки». Если вам не нужны метки, передайте пустой контейнер для обеих меток. Если вам нужна звезда или квадрат для одного из ползунков, у вас есть свобода выбора.

 import 'package:flutter/material.dart';

/// a range slider that fits the box it is given.
/// colors can be customized. Also, if thumbSlider,
/// leftLabel, or rightLabel is given, there is
/// greater option for customizability.
class CustomRangeSlider extends StatefulWidget {
  //expect "lowerValue" and "upperValue" as keys
  final Function(RangeSliderValues) onChange;
  final int min;
  final int max;
  final int lowerValue; // lower value that is selected.
  final int upperValue; // higher number that is selected
  final int barHeight; // height of the containers between thumb sliders
  final Color selectedColor;
  final Color deselectedColor;
  final Widget? leftThumbSlider; //by default, it is a blue circle.
  final Widget? rightThumbSlider; //by default, it is a blue circle.
  final Widget? leftLabel; //by default, it is blue text
  final Widget? rightLabel; //by default, it is blue text

  const CustomRangeSlider(
      {Key? key,
      required this.onChange,
      this.max = 10,
      this.min = 0,
      this.lowerValue = 0,
      this.upperValue = 10,
      this.barHeight = 2,
      this.selectedColor = Colors.blue,
      this.deselectedColor = Colors.grey,
      this.leftThumbSlider,
      this.rightThumbSlider,
      this.leftLabel,
      this.rightLabel})
      : super(key: key);

  @override
  _CustomRangeSliderState createState() => _CustomRangeSliderState();
}

class _CustomRangeSliderState extends State<CustomRangeSlider> {
  late int lowerValue = 0;
  late int upperValue = 0;
  late Widget leftThumbSlider;
  late Widget rightThumbSlider;
  late Widget leftLabel;
  late Widget rightLabel;
  int? dragStartVal;

  @override
  void initState() {
    if (widget.leftThumbSlider != null) {
      leftThumbSlider = widget.leftThumbSlider!;
    } else {
      leftThumbSlider = getDefaultThumbSlider(key: Key("leftThumbSlider"));
    }
    if (widget.rightThumbSlider != null) {
      rightThumbSlider = widget.rightThumbSlider!;
    } else {
      rightThumbSlider = getDefaultThumbSlider(key: Key("rightThumbSlider"));
    }
    if (widget.leftLabel != null) {
      leftLabel = widget.leftLabel!;
    } else {
      leftLabel = getDefaultLabel();
    }
    if (widget.rightLabel != null) {
      rightLabel = widget.rightLabel!;
    } else {
      rightLabel = getDefaultLabel(isLeft: false);
    }
    lowerValue = widget.lowerValue;
    upperValue = widget.upperValue;
    super.initState();
  }

  Widget getDefaultThumbSlider({Key? key}) {
    return Container(
      key: key,
      height: 25,
      width: 25,
      decoration: BoxDecoration(
        color: widget.selectedColor,
        borderRadius: BorderRadius.all(
          Radius.circular(50),
        ),
      ),
    );
  }

  void onChange() {
    widget.onChange(
        RangeSliderValues(lowerValue: lowerValue, upperValue: upperValue));
  }

  Widget getDefaultLabel({bool isLeft = true}) {
    return Container(
      width: 25,
      child: Center(
        child: Text(
          isLeft ? "$lowerValue" : "$upperValue",
          style: TextStyle(
              color: widget.selectedColor,
              fontSize: 11,
              fontWeight: FontWeight.w400),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      return Column(
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.end,
            children: [
              Expanded(
                flex: lowerValue - widget.min,
                child: Container(),
              ),
              getDefaultLabel(),
              Expanded(
                flex: upperValue - lowerValue,
                child: Container(),
              ),
              getDefaultLabel(isLeft: false),
              Expanded(
                flex: widget.max - upperValue,
                child: Container(),
              ),
            ],
          ),
          SizedBox(
            height: 2,
          ),
          Row(
            children: [
              Expanded(
                flex: lowerValue - widget.min,
                child: Container(
                  height: widget.barHeight * 1,
                  color: widget.deselectedColor,
                ),
              ),
              Column(
                children: [
                  GestureDetector(
                    onHorizontalDragStart: (DragStartDetails details) {
                      dragStartVal = lowerValue;
                    },
                    onHorizontalDragEnd: (DragEndDetails details) {
                      dragStartVal = null;
                    },
                    onHorizontalDragUpdate: (DragUpdateDetails details) {
                      double totalDistanceTraveled = details.localPosition.dx;
                      double distanceBetweenValues =
                          constraints.maxWidth / (widget.max - widget.min);
                      int numSteps =
                          (totalDistanceTraveled / distanceBetweenValues)
                              .round();
                      int beforeChange = lowerValue;
                      if (dragStartVal != null) {
                        setState(() {
                          lowerValue = dragStartVal!   numSteps;
                          if (lowerValue < widget.min) lowerValue = widget.min;
                          if (lowerValue >= upperValue) {
                            lowerValue = upperValue - 1;
                          }
                        });
                        if (beforeChange != lowerValue) onChange();
                      }
                    },
                    child: leftThumbSlider,
                  ),
                ],
              ),
              Expanded(
                flex: upperValue - lowerValue,
                child: Container(
                  height: widget.barHeight * 1,
                  color: widget.selectedColor,
                ),
              ),
              Column(
                children: [
                  GestureDetector(
                    onHorizontalDragStart: (DragStartDetails details) {
                      dragStartVal = upperValue;
                    },
                    onHorizontalDragEnd: (DragEndDetails details) {
                      dragStartVal = null;
                    },
                    onHorizontalDragUpdate: (DragUpdateDetails details) {
                      double totalDistanceTraveled = details.localPosition.dx;
                      double distanceBetweenValues =
                          constraints.maxWidth / (widget.max - widget.min);
                      int numSteps =
                          (totalDistanceTraveled / distanceBetweenValues)
                              .round();
                      int beforeChange = upperValue;
                      if (dragStartVal != null) {
                        setState(() {
                          upperValue = dragStartVal!   numSteps;
                          if (upperValue > widget.max) upperValue = widget.max;
                          if (upperValue <= lowerValue) {
                            upperValue = lowerValue   1;
                          }
                        });
                        if (beforeChange != upperValue) onChange();
                      }
                    },
                    child: rightThumbSlider,
                  ),
                ],
              ),
              Expanded(
                flex: widget.max - upperValue,
                child: Container(
                  height: widget.barHeight * 1,
                  color: widget.deselectedColor,
                ),
              ),
            ],
          ),
        ],
      );
    });
  }
}

class RangeSliderValues {
  int lowerValue;
  int upperValue;
  RangeSliderValues({required this.lowerValue, required this.upperValue});
}

  

Комментарии:

1. Мне нравится ваш подход, но метки не работают с этим фрагментом, поэтому мне нужно было это исправить. Но в любом случае, спасибо — это очень полезно.

2. @rgolovakha, не стесняйтесь предлагать редактирование с вашим исправлением.

3. Вы назначаете метки в строках 66, 71, но, согласно 124 и 129, они не используются. Несмотря на это — я не смог заставить его работать таким образом. Но на самом деле моя потребность была удовлетворена просто предоставлением аргумента TextStyle для обеих меток. Чтобы расширить эту опцию, я думаю, она может работать, если передать «родительский» для ваших меток (отступы, границы и т. Д.) — Но я не проверял это.

Ответ №2:

Использовать mainAxisAlignment: MainAxisAlignment.spaceBetween

Комментарии:

1. Это немного лучше, но все еще не выровнено идеально.

2. Вероятно, это потому, что ваш текст слишком длинный по сравнению с размером «точки» на ползунке. Можно ли удалить префикс «Bac» и просто сохранить » 2″, » 3″, и т.д.?

3. Да, я тоже думал об этом, но, к сожалению, я не могу уменьшить метку…

4. Тогда, возможно, вы могли бы рассмотреть возможность использования другого элемента управления (например, пары выпадающих списков)