Lalit Rawat
Lalit Rawat

Reputation: 1292

How to make slider smoother in Flutter

My slider jumps from one value to another. How can I make it movable in between the values or make it smoother in Flutter?

    Column(
      children: <Widget>[
        RangeSlider(
          min: 1,
          max: 40,
          divisions: 4,
          onChanged: onChange,
          values: RangeValues((startValue ?? 1).toDouble(), (endValue ?? 40).toDouble()),
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            Text('Beginner', style: Theme.of(context).textTheme.body2),
            Text('Ameateur', style: Theme.of(context).textTheme.body2),
            Text('Intermediate', style: Theme.of(context).textTheme.body2),
            Text('Advanced', style: Theme.of(context).textTheme.body2),
            Text('Professional', style: Theme.of(context).textTheme.body2),
          ],
        ),
      ],
    );

Upvotes: 1

Views: 4075

Answers (1)

Tim Klingeleers
Tim Klingeleers

Reputation: 3034

The divisions property makes it jump in 4 steps. Remove that and the slider will be fluent.

RangeSlider(
  min: 1,
  max: 40,
  // divisions: 4, // <-- remove this
  onChanged: onChange,
  values: RangeValues((startValue ?? 1).toDouble(), (endValue ?? 40).toDouble()),
),

Update after question clarification

Question is actually about having the thumbs being fluent while still snapping to discrete points on the range slider. The Flutter widget does not provide an easy way to achieve that, so there are two options.

Option 1: custom widget

The first is to create a new widget to get the exact behavior you need. This might be the best option if you need a lot of customisation.

Option 2: hack the RangeSlider widget

Use the onChangeEnd callback in addition to the onChanged callback to update the position of the thumb correctly. The onChanged callback is used to make it fluent, while the onChangeEnd callback is used to snap the thumb to the closest discrete value based on a division count.

A possible implementation (the discretize method can possibly be shorter/improved):

class _RangeSliderExampleState extends State<RangeSliderExample> {
  double _startValue;
  double _endValue;
  static const double _minValue = 1;
  static const double _maxValue = 40;
  static const double _divisions = 4;

  @override
  Widget build(BuildContext context) {
    return RangeSlider(
      min: _minValue,
      max: _maxValue,
      onChanged: (values) {
        setState(() {
          _startValue = values.start;
          _endValue = values.end;
        });
      },
      onChangeEnd: (values) {
        setState(() {
          _startValue = _discretize(values.start, _divisions);
          _endValue = _discretize(values.end, _divisions);
        });
      },
      values: RangeValues((_startValue ?? 1).toDouble(),
          (_endValue ?? 40).toDouble()),
    );
  }

  double _discretize(double value, double divisions) {
    double x = value - _minValue;
    double range = _maxValue - _minValue;

    double result = x / range;
    return (result * divisions).round() / divisions * range + _minValue; 
  }
}

Upvotes: 6

Related Questions