Flutter Slider and RangeSlider: Custom Value Pickers

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Slider and RangeSlider guide, with a single-value slider snapping to divisions and a two-thumb range slider showing a low-high RangeValues selection.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Slider and RangeSlider guide.

Sliders pick a number from a range by feel rather than by typing — volume, brightness, a price filter. Flutter's Slider uses the same controlled pattern as checkboxes: a value in state and an onChanged that you update with setState. Its sibling RangeSlider adds a second thumb for a low-to-high range. This guide covers the basic slider, snapping with divisions and labels, the range slider with RangeValues, styling with SliderTheme, and the onChangeEnd trick for expensive work, with paste-ready code.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Every snippet below is paste-ready against current stable Flutter. A slider shares its controlled pattern with the checkbox, radio, and switch widgets — value in, change out.

Follow me on Instagram@sagnikteaches

We'll build a basic slider, snap it to whole numbers, add a range slider, style it, and defer heavy work to onChangeEnd.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch a filter panel with sliders built live, the channel covers these input patterns in real apps.

Subscribe on YouTube@codingliquids

A basic slider

Slider takes a value (a double in state), a min and max, and an onChanged that updates the value. The thumb only moves because you call setState — the slider stores nothing itself.

double _volume = 40;

Slider(
  value: _volume,
  min: 0,
  max: 100,
  onChanged: (value) => setState(() => _volume = value),
)

Omit min and max and the range defaults to 0.0–1.0. Drag continuously like this and the slider reports fractional values, which is right for smooth controls like volume or opacity.

Snap to whole numbers

For discrete steps — a 1–5 rating, a count — set divisions to the number of equal steps. Add a label to float the current value above the thumb as the user drags.

Slider(
  value: _rating,
  min: 0,
  max: 10,
  divisions: 10,           // snaps to whole numbers
  label: _rating.round().toString(),
  onChanged: (value) => setState(() => _rating = value),
)

With ten divisions across a 0–10 range the slider clicks to each integer. Without divisions it stays continuous, so reach for divisions whenever the value should be a clean step rather than a fraction.

The Complete Flutter Guide course thumbnail

Build interactive, filterable UIs

The Complete Flutter Guide covers sliders, filters, and full forms inside real, shipped apps.

Enrol now

A range slider

For a low-and-high bound — a price filter, an age range — use RangeSlider with a RangeValues holding a start and end. It shows two thumbs the user drags independently.

RangeValues _price = const RangeValues(20, 80);

RangeSlider(
  values: _price,
  min: 0,
  max: 100,
  divisions: 20,
  labels: RangeLabels('${_price.start.round()}', '${_price.end.round()}'),
  onChanged: (values) => setState(() => _price = values),
)

Read _price.start and _price.end for the chosen range — exactly what a "£20 to £80" product filter needs. The same divisions and labels options apply, now as a pair.

Styling with SliderTheme

Track colour, thumb shape, and tick marks come from SliderTheme. Wrap the slider in a SliderTheme with a SliderThemeData to override the active and inactive track colours, the thumb size, or the overlay. On Material 3 the defaults already follow your colour scheme, so you usually only adjust a colour or two rather than rebuild the whole look.

Defer heavy work with onChangeEnd

onChanged fires on every frame of the drag, which you need for a smooth thumb — but it's the wrong place for a network call or a database query. onChangeEnd fires once, when the drag finishes, so run expensive work there.

Slider(
  value: _price,
  min: 0,
  max: 1000,
  onChanged: (v) => setState(() => _price = v),      // smooth UI
  onChangeEnd: (v) => _fetchResultsForPrice(v),      // once, on release
)

This keeps the slider responsive while firing the costly operation only when the user settles — a small split that avoids hammering an API on every pixel of movement.

Common mistakes

  • Forgetting setState in onChanged. The thumb won't move; the slider is controlled.
  • A value outside min–max. The value must stay within the range or it asserts.
  • Expensive work in onChanged. It runs every frame; use onChangeEnd for network or database calls.
  • No divisions for discrete values. Continuous sliders report fractions; add divisions to snap.
  • An int instead of a double. Slider values are doubles; round only when you read them.

Frequently asked questions

How do I add a slider in Flutter?

Use Slider with a value double, min, max, and an onChanged that updates the value with setState.

How do I make a Flutter Slider snap to whole numbers?

Set divisions to the number of steps between min and max, and add a label to show the current value.

How do I create a range slider in Flutter?

Use RangeSlider with a RangeValues of start and end; read values.start and values.end for the range.

What is the difference between onChanged and onChangeEnd on a Flutter Slider?

onChanged fires continuously while dragging; onChangeEnd fires once on release. Use the latter for expensive work like API calls.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — Slider, RangeSlider, RangeValues, RangeLabels, and SliderTheme / SliderThemeData API references (docs.flutter.dev). Verified against current stable Flutter.