Search code examples
flutterflutter-layoutsliderflutter-dependenciesseekbar

UI - Customizing Slider Thumbh in Flutter


I am using Slider in Flutter for value selections but it needs to be customised as below : enter image description here

Normally I can create default slider but how can I achieve this Thumbh UI on my slider? Thanks.


Solution

  • The following would do the trick. You have to customize the SliderThemeData.thumbShape and SliderThemeData.trackShape properties to achieve that.

    Screenshot

    Here's a minimal reproducible example (Also, live demo on DartPad)

    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 const MaterialApp(
          title: 'Flutter Demo',
          home: MyHomePage(),
          debugShowCheckedModeBanner: false,
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key}) : super(key: key);
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      double sliderValue = 0;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Padding(
                  padding: EdgeInsets.only(left: 24.0),
                  child: Text(
                    'Font Size',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 16,
                    ),
                  ),
                ),
                Theme(
                  data: Theme.of(context).copyWith(
                    sliderTheme: const SliderThemeData(
                      thumbShape: MySliderComponentShape(),
                      trackShape: SameHeightRoundedSliderTrackShape(),
                      trackHeight: 8,
                    ),
                  ),
                  child: Slider(
                    onChanged: (value) => setState(() => sliderValue = value),
                    value: sliderValue,
                    min: 0,
                    max: 2,
                    divisions: 2,
                    activeColor: const Color.fromARGB(255, 231, 231, 231),
                    inactiveColor: const Color.fromARGB(255, 231, 231, 231),
                  ),
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: const [
                    Padding(
                      padding: EdgeInsets.only(left: 24),
                      child: Text('Small', style: TextStyle(fontSize: 14)),
                    ),
                    Text('Medium', style: TextStyle(fontSize: 14)),
                    Padding(
                      padding: EdgeInsets.only(right: 24),
                      child: Text('Large', style: TextStyle(fontSize: 14)),
                    ),
                  ],
                )
              ],
            ),
          ),
        );
      }
    }
    
    class MySliderComponentShape extends SliderComponentShape {
      const MySliderComponentShape();
    
      @override
      Size getPreferredSize(bool isEnabled, bool isDiscrete) {
        return const Size(34, 34);
      }
    
      @override
      void paint(PaintingContext context, Offset center,
          {required Animation<double> activationAnimation,
          required Animation<double> enableAnimation,
          required bool isDiscrete,
          required TextPainter labelPainter,
          required RenderBox parentBox,
          required SliderThemeData sliderTheme,
          required TextDirection textDirection,
          required double value,
          required double textScaleFactor,
          required Size sizeWithOverflow}) {
        final Canvas canvas = context.canvas;
        canvas.drawShadow(
            Path()
              ..addRRect(RRect.fromRectAndRadius(
                Rect.fromCenter(center: center, width: 38, height: 34),
                const Radius.circular(19),
              )),
            Colors.black,
            5,
            false);
        canvas.drawRRect(
          RRect.fromRectAndRadius(
            Rect.fromCenter(center: center, width: 34, height: 34),
            const Radius.circular(17),
          ),
          Paint()..color = const Color.fromARGB(255, 252, 241, 216),
        );
        canvas.drawRRect(
          RRect.fromRectAndRadius(
            Rect.fromCenter(center: center, width: 26, height: 26),
            const Radius.circular(13),
          ),
          Paint()..color = const Color.fromARGB(255, 235, 151, 72),
        );
      }
    }
    
    class SameHeightRoundedSliderTrackShape extends RoundedRectSliderTrackShape {
      const SameHeightRoundedSliderTrackShape();
    
      @override
      void paint(
        PaintingContext context,
        Offset offset, {
        required RenderBox parentBox,
        required SliderThemeData sliderTheme,
        required Animation<double> enableAnimation,
        required TextDirection textDirection,
        required Offset thumbCenter,
        bool isDiscrete = false,
        bool isEnabled = false,
        double additionalActiveTrackHeight = 0,
      }) {
        super.paint(
          context,
          offset,
          parentBox: parentBox,
          sliderTheme: sliderTheme,
          enableAnimation: enableAnimation,
          textDirection: textDirection,
          thumbCenter: thumbCenter,
          isDiscrete: isDiscrete,
          isEnabled: isEnabled,
          additionalActiveTrackHeight: additionalActiveTrackHeight,
        );
      }
    }