Slider
GlassSlider is a continuous 0 to 1 slider whose knob turns into a clear glass drop while you drag it, for volume, brightness or a scrubber.
GlassSlider · SliderGeometry · kGlassDropScale · kGlassDisabledOpacity · Source
GlassSlider picks a value between 0 and 1. It is 44 px tall and as wide as its parent lets it be.
At rest the knob is a white capsule on a thin track; while you press or drag it, the knob lifts into a
clear glass drop, like the switch's.
Tapping anywhere on the track jumps the value there. Screen readers can step it up and down.
When to use
- Continuous values: volume, brightness, a playback scrubber, a blur radius.
- Not for discrete steps. There is no snapping; if you need steps, round the value yourself or use a segmented control.
- Not for values you need to type exactly. Pair it with a readout, or use a text field.
Usage
The slider is controlled. It needs a bounded width, so in a Row put it in an Expanded:
Row(
children: <Widget>[
const Icon(Icons.volume_down),
Expanded(
child: GlassSlider(
value: _volume,
onChanged: (double v) => setState(() => _volume = v),
onChangeEnd: (double v) => save(v),
semanticLabel: 'Volume',
),
),
const Icon(Icons.volume_up),
],
)
onChanged fires on every frame of a drag. Use onChangeStart / onChangeEnd for work that should
happen once, such as saving. onChanged: null disables the slider (50% opacity).
Accessibility
Give every slider a semanticLabel ("Volume", "Brightness"). semanticStep (default 0.1) is how far
one increase or decrease moves it; 0.05 gives a screen reader 20 steps.
Performance
The fill of the track ends under the drop and follows it, so every frame of a drag changes what is under the glass, and the host captures again each frame. That is expected and is the price of a slider over glass. At rest it costs nothing.
Lining things up with the fill
SliderGeometry.fillEnd(value, width) returns the x position where the fill ends on a slider of
that width. Use it to place a tick, a label or a buffered-range bar exactly under the knob's centre.
Code
import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';
/// Volume and brightness on a glass card, with a readout.
/// Assumes a GlassHost above the navigator (MaterialApp.builder).
class DisplayControls extends StatefulWidget {
const DisplayControls({super.key});
@override
State<DisplayControls> createState() => _DisplayControlsState();
}
class _DisplayControlsState extends State<DisplayControls> {
double _volume = 0.6;
double _brightness = 0.8;
Widget _slider(String label, IconData icon, double value, ValueChanged<double> onChanged, Color color) => Row(
children: <Widget>[
Icon(icon, size: 20),
const SizedBox(width: 8),
Expanded(
child: GlassSlider(
value: value,
onChanged: onChanged,
onChangeEnd: (double v) => debugPrint('$label saved: $v'),
activeColor: color,
semanticLabel: label,
semanticStep: 0.05,
),
),
SizedBox(width: 44, child: Text('${(value * 100).round()}%', textAlign: TextAlign.end)),
],
);
@override
Widget build(BuildContext context) => SizedBox(
width: 360,
child: GlassCard(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_slider(
'Volume',
Icons.volume_up,
_volume,
(double v) => setState(() => _volume = v),
const Color(0xFF0A84FF),
),
_slider(
'Brightness',
Icons.light_mode,
_brightness,
(double v) => setState(() => _brightness = v),
const Color(0xFFFFD60A),
),
],
),
),
);
}API
| Parameter | Type | Default | Description |
|---|---|---|---|
value |
double |
required | The value, from 0 to 1 (clamped). |
onChanged |
ValueChanged<double>? |
required | Called while dragging. Null disables the slider (50% opacity). |
onChangeStart |
ValueChanged<double>? |
null |
A drag or tap began. |
onChangeEnd |
ValueChanged<double>? |
null |
A drag or tap ended. |
activeColor |
Color |
Color(0xFF0A84FF) |
The filled part of the track. |
trackColor |
Color |
Color(0x29787880) |
The rest of the track. |
dropScale |
double |
kGlassDropScale |
Size of the held drop relative to the knob. At least 1. |
dropWiden |
double |
0 |
How many px of the surroundings the drop pulls in. |
semanticLabel |
String? |
null |
Screen-reader label. |
semanticStep |
double |
0.1 |
How far one accessibility increase or decrease moves the value. Between 0 (exclusive) and 1. |
key |
Key? |
null |
Helpers
| Name | Description |
|---|---|
SliderGeometry.fillEnd(double value, double width) |
The x position, in px from the slider's left edge, where the fill ends for value on a slider width wide. |