Custom Shapes in Flutter Slider (SfSlider)

18 Nov 201820 minutes to read

This section explains how to customize shape elements of the Flutter Slider—track, thumb, divider, and ticks.

NOTE

You must import the theme.dart library from the Core package to use SfSliderTheme in all the examples shown below. Define each custom shape class outside your widget class so the Flutter Slider can reference it.

Track shape

You can change the size and shape of the track using the trackShape property in the SfSlider.

  • getPreferredSize() - Returns the size based on the values passed to it.
  • paint() - Used to change the track shape.

NOTE

  • You must use the thumbCenter and currentValue parameters of paint override method for customizing slider track.
  • You must use the startThumbCenter, endThumbCenter, and currentValues parameters of paint override method for customizing range slider and range selector track.
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class TrackShapePage extends StatefulWidget {
  @override
  _TrackShapePageState createState() => _TrackShapePageState();
}

class _TrackShapePageState extends State<TrackShapePage> {
  double _value = 5.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       body: Center(
          child: SfSliderTheme(
            data: SfSliderThemeData(
              activeTrackHeight: 10,
              inactiveTrackHeight: 10,
            ),
            child: SfSlider(
              min: 0.0,
              max: 10.0,
              value: _value,
              trackShape: _SfTrackShape(),
              onChanged: (double newValue) {
                setState(() {
                  _value = newValue;
                });
              },
            ),
          ),
        ),
     );
  }
}

class _SfTrackShape extends SfTrackShape {
  @override
  void paint(PaintingContext context, Offset offset, Offset? thumbCenter,
      Offset? startThumbCenter, Offset? endThumbCenter,
      {required RenderBox parentBox,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Animation<double> enableAnimation,
      required Paint? inactivePaint,
      required Paint? activePaint,
      required TextDirection textDirection}) {
    Paint paint = Paint()
      ..color = themeData.activeTrackColor!
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1;
    super.paint(context, offset, thumbCenter, startThumbCenter, endThumbCenter,
        parentBox: parentBox,
        themeData: themeData,
        enableAnimation: enableAnimation,
        inactivePaint: inactivePaint,
        activePaint: paint,
        textDirection: textDirection);
  }
}

Track shape

Thumb shape

You can change the size and shape of the thumb using the thumbShape property in the SfSlider.

  • getPreferredSize() - Returns the size based on the values passed to it.
  • paint() - Used to change the thumb shape.

NOTE

  • You must use the currentValue parameter of paint override method for customizing slider thumb.
  • You must use the currentValues parameter of paint override method for customizing range slider and range selector thumbs.

IMPORTANT

The themeData.activeTrackColor may be null. The null assertion operator (!) is used here for brevity; in production code, prefer a fallback (e.g., themeData.activeTrackColor ?? Colors.red) to avoid null errors.

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class ThumbShapePage extends StatefulWidget {
  @override
  _ThumbShapePageState createState() => _ThumbShapePageState();
}

class _ThumbShapePageState extends State<ThumbShapePage> {
  double _value = 4.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       body: Center(
          child: SfSliderTheme(
            data: SfSliderThemeData(overlayRadius: 0),
            child: SfSlider(
              min: 2.0,
              max: 10.0,
              value: _value,
              thumbShape: _SfThumbShape(),
              onChanged: (double newValue) {
                setState(() {
                  _value = newValue;
                });
              },
            ),
          ),
        ),
     );
  }
}

class _SfThumbShape extends SfThumbShape {
  @override
  void paint(PaintingContext context, Offset center,
      {required RenderBox parentBox,
      required RenderBox? child,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Paint? paint,
      required Animation<double> enableAnimation,
      required TextDirection textDirection,
      required SfThumb? thumb}) {
      final Path path = Path();

      path.moveTo(center.dx, center.dy);
      path.lineTo(center.dx + 10, center.dy - 15);
      path.lineTo(center.dx - 10, center.dy - 15);
      path.close();
      context.canvas.drawPath(
          path,
          Paint()
            ..color = themeData.activeTrackColor!
            ..style = PaintingStyle.fill
            ..strokeWidth = 2);
  }
}

Thumb shape

Divider shape

You can change the size and shape of the divider using the dividerShape property in the SfSlider.

  • getPreferredSize() - Returns the size based on the values passed to it.
  • paint() - Used to change the divider shape.

NOTE

  • You must use the thumbCenter and currentValue parameters of paint override method for customizing slider divider.
  • You must use the startThumbCenter, endThumbCenter, and currentValues parameters of paint override method for customizing range slider and range selector divider.
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class DividerShapePage extends StatefulWidget {
  @override
  _DividerShapePageState createState() => _DividerShapePageState();
}

class _DividerShapePageState extends State<DividerShapePage> {
  double _value = 6.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       body: SfSlider(
          min: 2.0,
          max: 10.0,
          value: _value,
          interval: 1,
          showDividers: true,
          dividerShape: _DividerShape(),
          onChanged: (double newValue) {
            setState(() {
              _value = newValue;
            });
          },
        ),
     );
  }
}

class _DividerShape extends SfDividerShape {
  @override
  void paint(PaintingContext context, Offset center, Offset? thumbCenter,
      Offset? startThumbCenter, Offset? endThumbCenter,
      {required RenderBox parentBox,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Paint? paint,
      required Animation<double> enableAnimation,
      required TextDirection textDirection}) {
    bool isActive = false;

    switch (textDirection) {
      case TextDirection.ltr:
        isActive = center.dx <= thumbCenter!.dx;
        break;
      case TextDirection.rtl:
        isActive = center.dx >= thumbCenter!.dx;
        break;
    }

    context.canvas.drawRect(
        Rect.fromCenter(center: center, width: 5.0, height: 10.0),
        Paint()
          ..isAntiAlias = true
          ..style = PaintingStyle.fill
          ..color = isActive ? themeData.activeTrackColor! : Colors.white);
  }
}

Divider shape

Major and minor ticks shapes

You can change the size and shape of the major and minor ticks using the tickShape and minorTickShape properties in the SfSlider.

  • getPreferredSize() - Returns the size based on the values passed to it.
  • paint() - Used to change the ticks shape.

NOTE

  • You must use the thumbCenter and currentValue parameters of paint override method for customizing slider ticks.
  • You must use the startThumbCenter, endThumbCenter, and currentValues parameters of paint override method for customizing range slider and range selector ticks.
import 'dart:math' as math;

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

class TicksShapePage extends StatefulWidget {
  @override
  _TicksShapePageState createState() => _TicksShapePageState();
}

class _TicksShapePageState extends State<TicksShapePage> {
  double _value = 5.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       body: SfSlider(
          min: 0.0,
          max: 10.0,
          value: _value,
          interval: 1,
          showTicks: true,
          minorTicksPerInterval: 3,
          tickShape: _SfTickShape(),
          minorTickShape: _SfMinorTickShape(),
          onChanged: (double newValue) {
            setState(() {
              _value = newValue;
            });
          },
        ),
     );
  }
}

class _SfTickShape extends SfTickShape {
  @override
  void paint(PaintingContext context, Offset offset, Offset? thumbCenter,
      Offset? startThumbCenter, Offset? endThumbCenter,
      {required RenderBox parentBox,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Animation<double> enableAnimation,
      required TextDirection textDirection}) {
    final Size tickSize = getPreferredSize(themeData);
    final bool isTickRightOfThumb = offset.dx > thumbCenter!.dx;
    final Color begin = isTickRightOfThumb
        ? themeData.disabledInactiveTickColor
        : themeData.disabledActiveTickColor;
    final Color end = isTickRightOfThumb
        ? themeData.inactiveTickColor
        : themeData.activeTickColor;
    final Paint paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = tickSize.width
      ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!;

    context.canvas.drawLine(
        Offset(
            offset.dx,
            offset.dy -
                2 -
                math.max(themeData.activeTrackHeight,
                    themeData.inactiveTrackHeight)),
        Offset(
            offset.dx,
            offset.dy -
                2 -
                math.max(themeData.activeTrackHeight,
                    themeData.inactiveTrackHeight) -
                tickSize.height),
        paint);
  }
}

class _SfMinorTickShape extends SfTickShape {
  @override
  void paint(PaintingContext context, Offset offset, Offset? thumbCenter,
      Offset? startThumbCenter, Offset? endThumbCenter,
      {required RenderBox parentBox,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Animation<double> enableAnimation,
      required TextDirection textDirection}) {
    final Size minorTickSize = getPreferredSize(themeData);
    final bool isMinorTickRightOfThumb = offset.dx > thumbCenter!.dx;

    final Color begin = isMinorTickRightOfThumb
        ? themeData.disabledInactiveMinorTickColor
        : themeData.disabledActiveMinorTickColor;
    final Color end = isMinorTickRightOfThumb
        ? themeData.inactiveMinorTickColor
        : themeData.activeMinorTickColor;
    final Paint paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = minorTickSize.width
      ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!;

    context.canvas.drawLine(
        Offset(
            offset.dx,
            offset.dy -
                2 -
                math.max(themeData.activeTrackHeight,
                    themeData.inactiveTrackHeight)),
        Offset(
            offset.dx,
            offset.dy -
                2 -
                math.max(themeData.activeTrackHeight,
                    themeData.inactiveTrackHeight) -
                minorTickSize.height),
        paint);
  }
}

Ticks shape