Range slider is a component that allows the users to select a value range. It's suitable if you have an input where the value is a range. With this component, the users set the minimum and maximum values by performing slides.
A range slider consists of several parts. It has two thumbs, each representing the minimum value and maximum value. Those thumbs can be dragged over a track. The track segment between the two thumbs is called active segment, while the rest are called inactive segments. It can also have value indicators which describe the value of each thumb. In addition, the track can be either continuous or discrete.
In Flutter, you can create such a component by using a widget called RangeSlider
. It provides plenty of customizations which enable you to change the behavior or appearance of the slider. Read this tutorial to see the examples.
Using RangeSlider
The constructor of RangeSlider
can be seen below.
RangeSlider({
Key? key,
required RangeValues values,
required ValueChanged<RangeValues>? onChanged,
ValueChanged<RangeValues>? onChangeStart,
ValueChanged<RangeValues>? onChangeEnd,
double min = 0.0,
double max = 1.0,
int? divisions,
RangeLabels? labels,
Color? activeColor,
Color? inactiveColor,
MaterialStateProperty<Color?>? overlayColor,
MaterialStateProperty<MouseCursor?>? mouseCursor,
SemanticFormatterCallback? semanticFormatterCallback,
})
Basic Usage
For the basic usage, you only need to pass two arguments, values
and onChanged
. The values
argument is used to determine the currently selected values (the range). You need to pass a RangeValues
instance. The RangeValues
class has a constructor with two positional arguments whose type is double
, representing the start and end values.
const RangeValues(double start, double end);
Usually, you need to store it as a state variable, so it can be updated later when the user drags the thumbs. The default minimum and maximum values are 0.0. and 1.0 respectively, so the initial value must be within the allowed range.
RangeValues _rangeValues = const RangeValues(0.45, 0.55);
The other required argument is onChanged
. It's used to handle the events when the selected range changes. It accepts a void function with one parameter of type RangeValues
, which is the currently selected values. If the value for values
argument is stored in a state variable, you need to update it inside the function. Setting the onChanged
argument to null
disables the slider.
RangeSlider(
values: _rangeValues,
onChanged: (RangeValues values) {
setState(() {
_rangeValues = values;
});
},
)
Output:
You need to be careful when updating the state variable since updating it may cause Flutter to also rebuild other widgets that have nothing to do with the slider value. As a solution, you can put the slider in a separate class or use a StatefulBuilder
.
Handle Drag Start and End
To handle when the user starts dragging a thumb, you can pass a void function as the onChangeStart
argument. The function also has a parameter of type RangeValues
. The similar argument for handling drag end events is onChangeEnd
. Although you can handle events using those two arguments, updating the state variable must be done inside the function passed the onChanged
argument. If you update it inside the onChangeEnd
's function for example, the position of the thumb will only be moved after the drag ends.
RangeSlider(
onChangeStart: (RangeValues values) {
print('onChangeStart');
},
onChangeEnd: (RangeValues values) {
print('onChangedEnd');
},
// other arguments
)
Set Minimum and Maximum Values
The minimum values and maximum values can be changed by passing the min
and max
arguments respectively. Just make sure the initial range is within the minimum and maximum values.
RangeSlider(
min: 0,
max: 100,
// other arguments
)
Set Custom Steps
By default, each thumb can be slid to any position in the track. However, it's also possible to make the slider has discrete steps. Therefore, the thumbs can only be moved to certain positions. It can be done by passing an integer as the divisions
argument. If the argument is passed, Flutter will automatically divide the track into several segments according to the argument value. There will be visible tick marks on each position where the thumbs can be dragged to. If the initial start or end value is not exactly on one of the tick marks, it will be moved to the nearest one.
RangeSlider(
divisions: 10,
// other arguments
)
Output:
Set Labels
You can add labels for the start and end thumbs when one of the thumbs is being dragged. To add the labels, pass a RangeLabels
value as the labels
argument. The RangeLabels
itself can be created using the constructor below.
const RangeLabels(String start, String end)
Usually, the displayed label is the current value of the respective thumb.
RangeSlider(
labels: RangeLabels(_rangeValues.start.toString(), _rangeValues.end.toString()),
// other arguments
)
Output:
Set Track and Thumb Colors
Flutter also provides some arguments to customize the colors. The color of the active segment can be set by passing a Color
as the activeColor
argument. It defaults to ColorScheme.primary
. The activeColor
value also affects the color of the thumb. Meanwhile, the argument to set the color of the inactive segments is inactiveColor
. It defaults to ColorScheme.primary
with an opacity of 0.24.
When a thumb is being dragged, the highlight color surrounding the thumb can be set by passing a MaterialStateProperty
as the overlayColor
argument. If this argument is null, the default value is activeColor
with an opacity of 0.12. If the activeColor
is also null, then it will use ColorScheme.primary
with an opacity of 0.12.
RangeSlider(
activeColor: Colors.purpleAccent,
inactiveColor: Colors.grey,
overlayColor: MaterialStateProperty.all(Colors.red),
// other arguments
)
Output:
Set SliderThemeData
There are a lot more customizations that you can apply if you use SliderThemeData
. However, despite providing more customizations, the styles set by SliderThemeData
can be overridden by the corresponding arguments passed to the constructor of SliderRange
.
SliderThemeData({
double? trackHeight,
Color? activeTrackColor,
Color? inactiveTrackColor,
Color? secondaryActiveTrackColor,
Color? disabledActiveTrackColor,
Color? disabledInactiveTrackColor,
Color? disabledSecondaryActiveTrackColor,
Color? activeTickMarkColor,
Color? inactiveTickMarkColor,
Color? disabledActiveTickMarkColor,
Color? disabledInactiveTickMarkColor,
Color? thumbColor,
Color? overlappingShapeStrokeColor,
Color? disabledThumbColor,
Color? overlayColor,
Color? valueIndicatorColor,
SliderComponentShape? overlayShape,
SliderTickMarkShape? tickMarkShape,
SliderComponentShape? thumbShape,
SliderTrackShape? trackShape,
SliderComponentShape? valueIndicatorShape,
RangeSliderTickMarkShape? rangeTickMarkShape,
RangeSliderThumbShape? rangeThumbShape,
RangeSliderTrackShape? rangeTrackShape,
RangeSliderValueIndicatorShape? rangeValueIndicatorShape,
ShowValueIndicator? showValueIndicator,
TextStyle? valueIndicatorTextStyle,
double? minThumbSeparation,
RangeThumbSelector? thumbSelector,
MaterialStateProperty<MouseCursor?>? mouseCursor,
})
As you can see from the constructor above, there are plenty of arguments that you can use to modify a RangeSlider
. However, since SliderThemeData
is also used for the Slider
widget, there are some arguments that don't have any effect on RangeSlider
.
There are two ways to use SliderThemeData
. First, you can wrap the RangeSlider
as the child of a SliderTheme
. Besides the child
argument, the SliderTheme
's constructor requires you to pass an argument named data
whose type is SliderThemeData
. It affects all RangeSlider
widgets under the tree of the SliderTheme
.
const SliderTheme({
Key? key,
required SliderThemeData data,
required Widget child,
})
The below examples creates a new SliderThemeData
.
SliderTheme(
data: SliderThemeData(
// arguments
),
child: RangeSlider(
// arguments
),
)
It's also possible to copy the existing SliderThemeData
from the BuildContext
using the copyWith
method.
SliderTheme(
data: SliderTheme.of(context).copyWith(
// arguments
),
child: RangeSlider(
// arguments
),
)
Another way is by setting it as the slider theme that affects all SliderRange
widgets in the application. You can do it by passing it as the sliderTheme
of a ThemeData
. The ThemeData
itself has to be set as the theme
of the MaterialApp
.The styles set using this method can be overridden by the theme data of a SliderTheme
widget.
MaterialApp(
title: 'Woolha.com Flutter Tutorial',
theme: ThemeData.light().copyWith(
sliderTheme: SliderThemeData(
),
)
Below I'm going to show you some of the customizations that can be applied using SliderThemeData
.
Set Track Height
The height of the track can be set using the trackHeight
argument.
SliderThemeData(
trackHeight: 8,
// other arguments
)
Output:
Set Track Colors
With SliderThemeData
, it's possible to set the colors for active and inactive segments by passing a Color
as the activeTrackColor
and inactiveTrackColor
arguments respectively. In addition, you can also set different colors for the track when the slider is disabled using disabledActiveTrackColor
and disabledInactiveTrackColor
arguments.
SliderThemeData(
activeTrackColor: Colors.pinkAccent,
inactiveTrackColor: Colors.yellow,
disabledActiveTrackColor: Colors.grey,
disabledInactiveTrackColor: Colors.grey.withOpacity(0.5),
// other arguments
)
Output:
Set Tick Mack Colors
The tick mark colors can be customized as well. Those in the active segment can by passing a Color
as the activeTickMarkColor
argument. For the tick marks in inactive segments, you can set a Color
value as the inactiveTickMarkColor
argument. The arguments to be used when the slider is disabled are disabledActiveTickMarkColor
and disabledInactiveTickMarkColor
SliderThemeData(
activeTickMarkColor: Colors.white,
inactiveTickMarkColor: Colors.white60,
disabledActiveTickMarkColor: Colors.black26,
disabledInactiveTickMarkColor: Colors.black12,
// other arguments
)
Output:
Set Tick Mark Shape
The shape of the tick marks can be changed by passing a RangeSliderTickMarkShape
instance as the rangeTickMarkShape
argument. The default value is RoundRangeSliderTickMarkShape
whose radius is 1/4 of the track height. The example below also uses RoundRangeSliderTickMarkShape
but with a different radius. If you want to use a different shape, you need to create a custom class that extends RangeSliderTickMarkShape
.
SliderThemeData(
rangeTickMarkShape: RoundRangeSliderTickMarkShape(
tickMarkRadius: 5,
),
// other arguments
)
Output:
Set Thumb Color
You can pass a Color
value as the thumbColor
argument to set the color of the thumbs.
SliderThemeData(
thumbColor: Colors.teal,
// other arguments
)
Output:
Set Thumb Shape
To change the thumb shape, you have to pass the rangeTickMarkShape
argument whose type is RangeSliderTickMarkShape
. It defaults to RoundSliderTickMarkShape
. The example below uses RoundRangeSliderTickMarkShape
with a custom radius. To use another shape other than rounded, create your own class that extends RangeSliderTickMarkShape
.
SliderThemeData(
rangeThumbShape: RoundRangeSliderThumbShape(
enabledThumbRadius: 15,
),
// other arguments
)
Output:
RangeSlider
Parameters
Key? key
: The widget's key, used to control how a widget is replaced with another.required RangeValues values
: The currently selected values.required ValueChanged<RangeValues>? onChanged
: Function to be called when the user is dragging the thumb to change the values.ValueChanged<RangeValues>? onChangeStart
: Function to be called when the user starts selecting a new range.ValueChanged<RangeValues>? onChangeEnd
: Function to be called when the user has done selecting a new range.double min
: The minimum value. Defaults to0.0
.double max
: The maximum value. Defaults to1.0
.RangeLabels? labels
: Labels to be displayed.Color? activeColor
: The color of the thumb and the track's active segment.Color? inactiveColor
: The color of the track's inactive segments.MaterialStateProperty<Color?>? overlayColor
: The highlight color which indicates that the thumb is hovered or dragged.MaterialStateProperty<MouseCursor?>? mouseCursor
: The mouse cursor when the pointer enters or is hovering over the widget.SemanticFormatterCallback? semanticFormatterCallback
: The callback used to create a semantic value from the slider's values..
SliderThemeData
Parameters
double? trackHeight
: The height of the track.Color? activeTrackColor
: The color of the track's active segment.Color? inactiveTrackColor
: The color of the track's inactive segments.Color? secondaryActiveTrackColor
: Doesn't have any effect onSliderRange
.Color? disabledActiveTrackColor
: The color of the track's active segment when disabled.Color? disabledInactiveTrackColor
: The color of the track's inactive segment when disabled.Color? disabledSecondaryActiveTrackColor
: Doesn't have any effect onSliderRange
.Color? activeTickMarkColor
: The color of tick marks in active segment.Color? inactiveTickMarkColor
: The color of tick marks in inactive segments.Color? disabledActiveTickMarkColor
: The color of tick marks in active segment when disabled.Color? disabledInactiveTickMarkColor
: The color of tick marks in inactive segment when disabled.Color? thumbColor
: Color of the thumbs.Color? overlappingShapeStrokeColor
: The color given to the top thumb's perimeter when the thumbs are overlapping.Color? disabledThumbColor
: Color of the thumbs when disabled.Color? overlayColor
: The color of the overlay around the thumb when pressed, focused, or hovered.Color? valueIndicatorColor
: The color for the value indicator.SliderComponentShape? overlayShape
: The shape for the thumbs' overlay.SliderTickMarkShape? tickMarkShape
: Doesn't have any effect onSliderRange
.SliderComponentShape? thumbShape
: Doesn't have any effect onSliderRange
.SliderTrackShape? trackShape
: Doesn't have any effect onSliderRange
.SliderComponentShape? valueIndicatorShape
: Doesn't have any effect onSliderRange
.RangeSliderTickMarkShape? rangeTickMarkShape
: The shape of the tick marks.RangeSliderThumbShape? rangeThumbShape
: The shape of the thumbs.RangeSliderTrackShape? rangeTrackShape
: The shape of the track.RangeSliderValueIndicatorShape? rangeValueIndicatorShape
: The shape of the value indicators.ShowValueIndicator? showValueIndicator
: Whether the value indicator should be shown.TextStyle? valueIndicatorTextStyle
: The text style of the value indicators.double? minThumbSeparation
: The minimum distance between the thumbs.RangeThumbSelector? thumbSelector
: A function that determines which thumb to be selected when the slider is interacted with.MaterialStateProperty<MouseCursor?>? mouseCursor
: The mouse cursor when the pointer enters or is hovering over the widget.
Full Code
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
theme: ThemeData.light().copyWith(
sliderTheme: const SliderThemeData(
// thumbColor: Colors.yellow,
)
),
home: Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
backgroundColor: Colors.teal,
),
body: const RangeSliderExample(),
),
);
}
}
class RangeSliderExample extends StatefulWidget {
const RangeSliderExample({super.key});
@override
State<StatefulWidget> createState() {
return _RangeSliderExampleState();
}
}
class _RangeSliderExampleState extends State<RangeSliderExample> {
RangeValues _rangeValues = const RangeValues(40, 60);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: SliderTheme(
// data: SliderTheme.of(context).copyWith(
data: SliderThemeData(
trackHeight: 8,
activeTrackColor: Colors.pinkAccent,
inactiveTrackColor: Colors.yellow,
disabledActiveTrackColor: Colors.grey,
disabledInactiveTrackColor: Colors.grey.withOpacity(0.5),
activeTickMarkColor: Colors.white,
inactiveTickMarkColor: Colors.white60,
disabledActiveTickMarkColor: Colors.black26,
disabledInactiveTickMarkColor: Colors.black12,
rangeTickMarkShape: const RoundRangeSliderTickMarkShape(
tickMarkRadius: 5,
),
thumbColor: Colors.teal,
rangeThumbShape: const RoundRangeSliderThumbShape(
enabledThumbRadius: 15,
),
),
child: Container(
child: RangeSlider(
min: 0,
max: 100,
divisions: 10,
labels: RangeLabels(_rangeValues.start.toString(), _rangeValues.end.toString()),
// activeColor: Colors.purpleAccent,
// inactiveColor: Colors.grey,
overlayColor: MaterialStateProperty.all(Colors.red),
values: _rangeValues,
onChangeStart: (RangeValues values) {
print('onChangeStart');
},
onChangeEnd: (RangeValues values) {
print('onChangedEnd');
},
onChanged: (RangeValues values) {
print('onChanged');
setState(() {
_rangeValues = values;
});
},
),
),
)
),
);
}
}
Summary
In this tutorial, we have learned how to create a range slider in Flutter using the RangeSlider
widget. It allows you to set the minimum and maximum values and set custom steps by dividing the track into several segments. The widget also allows you to customize the appearance by defining a SliderThemeData
.
You can also read about:
- How to use
Slider
widget in Flutter, in case you need a slider with a single value.