Material RangeSlider in Flutter

Anthony Robledo
Jul 11 · 7 min read

The range slider, a highly customizable component for selecting a range of values, has been released in Flutter 1.7. This article explains what a range slider is, why you might use it, and how you can use Material Theming to customize the behavior and appearance of the Flutter RangeSlider.

Why Range Slider?

Structure & Implementation

The RangeSlider consists of 5 parts:

  1. A track that the thumbs slide across.
  2. Tick marks on the track when the RangeSlider is discrete.
  3. 2 thumbs (or knobs) that indicate the min and max value of the range.
  4. Value indicators that show the labels for the thumb values when labels are defined and showValueIndicator matches the type of slider.
  5. Overlays that display on the thumbs when they are pressed.

We needed the RangeSlider to have rich animations. This includes the interaction-driven animations for the positions of the thumb, as well as the built-in animations for the overlay and value indicators. In Flutter, we do this by making the RangeSlider component a StatefulWidget, which stores the animation controllers as state.

The actual range slider values are stored as state in the parent widget. The values are updated by calling setState() within the RangeSlider’s onChange() callback. In other words, in order to have an interactive range slider, the RangeSlider widget itself must be created within a StatefulWidget.

The RangeSlider’s State object builds a LeafRenderObjectWidget. Everything is painted in its inner RenderBox, which also handles touch input.

Handling Touch Input

If you have no interest in peeking under the hood, feel free to skip this section.

To ensure that RangeSlider can handle both taps and drags while functioning properly within scroll views, tab bar views, and other widgets that handle gestures, a GestureArenaTeam is used. A GestureArenaTeam allows for a gesture within a group of gestures to be properly chosen by “winning.”

First, the drag recognizer is added to the team, followed by the tap recognizer. There is no team captain, so the drag recognizer wins, since it was the first recognizer added to the team, as soon as any other recognizers are out of the arena. On the other hand, if the tap can win outright, such as when the slider is within a vertically scrolling list and the user taps then immediately lifts, then the tap recognizer wins.

The drag and tap events resolve to 1 of 3 possible interactions:

  • Drag onStart or Tap onTapDown_startInteraction
  • Drag onUpdate_handleDragUpdate
  • Drag onEnd or onCancel and Tap onEnd or onCancel_endInteraction

At the start of interaction, one of the very first things that must be determined is which thumb should be selected for movement. The RangeSlider does this by using a themable function that takes in properties like the tap value and drag displacement, and returns a thumb choice: Thumb.start, Thumb.end, or null for no selection.

The default thumb selector first attempts to find the closest thumb in _startInteraction. If a thumb is selected, then the thumb’s position is immediately updated to the tap value. But if the tap value is between the thumbs, but not in either touch target, there is no selection. Also, if the thumbs are close enough together, and the tap is in both touch targets, no thumb is selected. In this case, a thumb is only selected once there is a non-zero movement (drag displacement). Then the left thumb is selected for negative movement, and the right thumb is selected for positive movement. This is the only scenario where the interaction actually begins in the first _handleDragUpdate step. In either case, a special callback, onChangeStart(), emits the start values of this interaction.

When the thumbs are further apart, touching the inner track does not select a thumb:

When the thumbs are closer together, the drag displacement is used to determine the thumb selection:

Implementation of the default thumb selector with the behavior described above:

After a thumb is selected, all future drag updates are used to determine the new positions of the thumb. The overlay animation starts on the selected thumb, and the value indicator animations start on both thumbs. As the user drags the selected thumb, the range slider emits a new set of values with the updated position, and the values are then passed back to the range slider to update its corresponding position.

The last step is _endInteraction. Once the tap or drag gesture is lifted, the overlay and value indicator animations that were started in the first step are reversed. A special callback, onChangeEnd(), also emits the end values.

Custom Touch Input Selection

Implementation of a custom thumb selector that always finds the closest thumb:

Once you have this custom thumb, you can set it in the global app theme:

Or it can be set on a specific slider instance using the SliderTheme:

Controlling Allowed Thumb Positions

If it is only necessary to restrict the thumbs for the sake of appearances, then the minThumbSeparation property can be used to limit the number of logical pixels that separate the 2 thumbs. The default top thumb will draw a white outline around itself for better contrast between the thumbs. Here is a side by side comparison showing the default value of 8 vs a custom value of 24

Painting Shapes

  1. Track
  2. Overlays
  3. Tick Marks (if discrete)
  4. Value Indicators (if visible)
  5. Thumbs

This can be important to know when painting custom shapes. All shape implementations are abstracted away from the RenderBox.paint() method through 5 separate abstract classes, which makes the painting or rendering of the RangeSlider fully customizable and themable since the classes exist on the SliderThemeData object.

In the next section, we will show how to override the default shapes with custom shapes.

Using Custom Shapes

This is done by passing custom implementations of the abstract shape classes into the SliderThemeData. This takes advantage of the RangeSliderThumbShape class to provide custom thumbs that have different appearances depending on what side they are on.

The custom range thumb shape can be implemented as follows:

Then the custom range thumb shape can be set on a SliderThemeData:

Closing Remarks

The complete code for all code included in this article, along with more examples, can be found in the Material gallery on github and in the Material library on github.

Special thanks to Shams Zakhour, Liam Spradlin, Barbara Eldredge, Cortney Cassidy, and Will Larche.


Flutter is Google's mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source. Learn more at

Anthony Robledo

Written by



Flutter is Google's mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source. Learn more at