SELECTION CONTROLS

Slider

A single, reusable Flutter slider built on SliderTheme so it inherits the FlutterKit house look. The active track fills lavender, the inactive track is an inset grey, and the white thumb carries a soft shadow. It supports a continuous drag, discrete divisions with a value bubble, an optional leading label row, and a disabled state. No external packages, fully DartPad-ready.

sliderrangeinput
Preview8 variants

Continuous

Start

Full

What's Included

  • Continuous slider with a lavender active track and inset-grey rest
  • White thumb with a soft shadow and a lavender press overlay
  • Discrete mode — divisions snap the thumb and show a value bubble
  • Optional label row with the live value on the right
  • Disabled look — muted track and thumb, drag ignored
  • One AppSlider wrapping SliderTheme so it inherits the house theme

Use Cases

  • Volume / brightness controls in a settings screen
  • Price or distance filters with discrete steps
  • Adjusting a numeric value — font size, opacity, zoom
  • Onboarding preferences captured on a 0–100 scale

PRO TIP

Reach for divisions only when the underlying value is genuinely discrete (a 1–5 rating, 10% steps) — a continuous control like brightness should stay smooth, since snapping fights the user. Always surface the current value (a bubble while dragging or a label beside the track) so the slider is never a guessing game, and round it in onChanged before you store it so your state matches what the user sees.

Complete Dart Code

Copy this into your Flutter project. No external packages required.

dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _volume = 60;
  double _rating = 3;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        backgroundColor: const Color(0xFFFAFAFB),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                AppSlider(
                  value: _volume,
                  label: 'Volume',
                  onChanged: (v) => setState(() => _volume = v),
                ),
                const SizedBox(height: 24),
                AppSlider(
                  value: _rating,
                  min: 1,
                  max: 5,
                  divisions: 4,
                  label: 'Rating',
                  onChanged: (v) => setState(() => _rating = v),
                ),
                const SizedBox(height: 24),
                const AppSlider(value: 40, label: 'Disabled', enabled: false),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// FlutterKit Slider — house design system (Linear lavender, light theme).
/// One value-driven slider built on SliderTheme; continuous or discrete.
/// rohansurve.in/flutterkit/slider

class AppSlider extends StatelessWidget {
  const AppSlider({
    super.key,
    required this.value,
    this.onChanged,
    this.min = 0,
    this.max = 100,
    this.divisions,
    this.label,
    this.enabled = true,
  });

  /// Current value, between [min] and [max].
  final double value;

  /// Called with the next value while dragging.
  final ValueChanged<double>? onChanged;

  /// Range bounds.
  final double min;
  final double max;

  /// Discrete steps — snaps the thumb and shows a value bubble.
  final int? divisions;

  /// Optional leading label; the live value shows on the right.
  final String? label;

  /// Disabled look — muted track and thumb, drag ignored.
  final bool enabled;

  // ── House tokens — docs/flutterkit-design.md §1 ──
  static const Color _accent = Color(0xFF5E6AD2);
  static const Color _surfaceInset = Color(0xFFEEEFF2);
  static const Color _surfaceMuted = Color(0xFFF4F5F7);
  static const Color _ink = Color(0xFF1C1E26);
  static const Color _inkMuted = Color(0xFF51555E);
  static const Color _inkTertiary = Color(0xFFB4B8BF);

  @override
  Widget build(BuildContext context) {
    final Color active = enabled ? _accent : _surfaceMuted;
    final Color thumb = enabled ? Colors.white : const Color(0xFFF7F7F8);

    final slider = SliderTheme(
      data: SliderThemeData(
        trackHeight: 5,
        activeTrackColor: active,
        inactiveTrackColor: _surfaceInset,
        thumbColor: thumb,
        overlayColor: const Color(0x1F5E6AD2),
        valueIndicatorColor: _accent,
        valueIndicatorTextStyle: const TextStyle(
          color: Colors.white,
          fontSize: 12,
          fontWeight: FontWeight.w600,
        ),
        thumbShape: const RoundSliderThumbShape(
          enabledThumbRadius: 10,
          elevation: 2,
          pressedElevation: 4,
        ),
        overlayShape: const RoundSliderOverlayShape(overlayRadius: 20),
        trackShape: const RoundedRectSliderTrackShape(),
        showValueIndicator: ShowValueIndicator.onlyForDiscrete,
      ),
      child: Slider(
        value: value.clamp(min, max),
        min: min,
        max: max,
        divisions: divisions,
        label: divisions != null ? value.round().toString() : null,
        onChanged: enabled ? onChanged : null,
      ),
    );

    if (label == null) return slider;

    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 4),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                label!,
                style: TextStyle(
                  color: enabled ? _inkMuted : _inkTertiary,
                  fontSize: 13,
                  fontWeight: FontWeight.w500,
                ),
              ),
              Text(
                value.round().toString(),
                style: TextStyle(
                  color: enabled ? _ink : _inkTertiary,
                  fontSize: 13,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ],
          ),
        ),
        slider,
      ],
    );
  }
}

// ── Usage ──
// AppSlider(value: v, onChanged: (n) {})                            // continuous
// AppSlider(value: v, min: 1, max: 5, divisions: 4, onChanged: (n){})// discrete
// AppSlider(value: v, label: 'Volume', onChanged: (n) {})           // labelled
// const AppSlider(value: 40, enabled: false)                        // disabled

How to use this widget

  1. 1Copy the complete Dart code above
  2. 2Create a new file — app_slider.dart in your Flutter project
  3. 3Use AppSlider(value: v, onChanged: (n) {}) — switch the look with the min/max, divisions, label, and enabled parameters

Related components

Selection Controls

Switch Toggle

Animated on/off switch — lavender track, two sizes, with label and disabled states

View component →
Selection Controls

Checkbox

Tristate checkbox — unchecked, checked & indeterminate, with label, disabled & error states

View component →
Avatars & Chips

Chip

One enum-driven chip — basic, input, choice & status kinds, lavender selected state

View component →