Created
November 13, 2024 21:20
-
-
Save hectorAguero/fae883c60ea22db3b2f238e85d22b33d to your computer and use it in GitHub Desktop.
Shadcn like RangeSlider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) => const MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
body: Center(child: ShadSlider(initialValue: RangeValues(1, 10))), | |
), | |
); | |
} | |
class ShadSliderController extends ValueNotifier<RangeValues> { | |
ShadSliderController({ | |
required RangeValues initialValue, | |
}) : super(initialValue); | |
} | |
class ShadSlider extends StatefulWidget { | |
const ShadSlider({ | |
super.key, | |
this.initialValue, | |
this.onChanged, | |
this.enabled = true, | |
this.mouseCursor, | |
this.disabledMouseCursor, | |
this.thumbColor, | |
this.disabledThumbColor, | |
this.thumbBorderColor, | |
this.disabledThumbBorderColor, | |
this.activeTrackColor, | |
this.inactiveTrackColor, | |
this.disabledActiveTrackColor, | |
this.disabledInactiveTrackColor, | |
this.trackHeight, | |
this.thumbRadius, | |
this.onChangeStart, | |
this.onChangeEnd, | |
this.divisions, | |
this.semanticFormatterCallback, | |
this.controller, | |
}) : assert( | |
(initialValue != null) ^ (controller != null), | |
'Either initialValue or controller must be specified', | |
); | |
final RangeValues? initialValue; | |
final ValueChanged<RangeValues>? onChanged; | |
final bool enabled; | |
final MouseCursor? mouseCursor; | |
final MouseCursor? disabledMouseCursor; | |
final Color? thumbColor; | |
final Color? disabledThumbColor; | |
final Color? thumbBorderColor; | |
final Color? disabledThumbBorderColor; | |
final Color? activeTrackColor; | |
final Color? inactiveTrackColor; | |
final Color? disabledActiveTrackColor; | |
final Color? disabledInactiveTrackColor; | |
final double? trackHeight; | |
final double? thumbRadius; | |
final ValueChanged<double>? onChangeStart; | |
final ValueChanged<double>? onChangeEnd; | |
final int? divisions; | |
final SemanticFormatterCallback? semanticFormatterCallback; | |
final ShadSliderController? controller; | |
@override | |
State<ShadSlider> createState() => _ShadSliderState(); | |
} | |
class _ShadSliderState extends State<ShadSlider> { | |
late final controller = widget.controller ?? | |
ShadSliderController( | |
initialValue: widget.initialValue!, | |
); | |
@override | |
void initState() { | |
super.initState(); | |
controller.addListener(onChanged); | |
} | |
@override | |
void dispose() { | |
controller.removeListener(onChanged); | |
// dispose the internal controller | |
if (widget.controller == null) { | |
controller.dispose(); | |
} | |
super.dispose(); | |
} | |
void onChanged() { | |
widget.onChanged?.call(controller.value); | |
} | |
@override | |
Widget build(BuildContext context) { | |
final mTheme = Theme.of(context); | |
final effectiveMouseCursor = widget.mouseCursor ?? SystemMouseCursors.click; | |
final effectiveDisabledMouseCursor = | |
widget.disabledMouseCursor ?? SystemMouseCursors.forbidden; | |
final effectiveThumbColor = widget.thumbColor ?? | |
mTheme.sliderTheme.thumbColor ?? | |
mTheme.colorScheme.surface; | |
final effectiveMin = widget.initialValue?.start ?? 0; | |
final effectiveMax = widget.initialValue?.end ?? 1; | |
final effectiveThumbBorderColor = | |
widget.thumbBorderColor ?? mTheme.colorScheme.primary; | |
final effectiveDisabledThumbColor = widget.disabledThumbColor ?? | |
mTheme.sliderTheme.disabledThumbColor ?? | |
mTheme.colorScheme.surface; | |
final effectiveDisabledThumbBorderColor = widget.disabledThumbBorderColor ?? | |
mTheme.colorScheme.primary.withOpacity(.5); | |
final effectiveActiveTrackColor = widget.activeTrackColor ?? | |
mTheme.sliderTheme.activeTrackColor ?? | |
mTheme.colorScheme.primary; | |
final effectiveInactiveTrackColor = widget.inactiveTrackColor ?? | |
mTheme.sliderTheme.inactiveTrackColor ?? | |
mTheme.colorScheme.secondary; | |
final effectiveDisabledActiveTrackColor = widget.disabledActiveTrackColor ?? | |
mTheme.sliderTheme.disabledActiveTrackColor ?? | |
mTheme.colorScheme.primary.withOpacity(.5); | |
final effectiveDisabledInactiveTrackColor = | |
widget.disabledInactiveTrackColor ?? | |
mTheme.sliderTheme.disabledInactiveTrackColor ?? | |
mTheme.colorScheme.secondary.withOpacity(.5); | |
final effectiveTrackHeight = | |
widget.trackHeight ?? mTheme.sliderTheme.trackHeight ?? 8; | |
final effectiveThumbRadius = widget.thumbRadius ?? 10.0; | |
return Theme( | |
data: mTheme.copyWith( | |
sliderTheme: mTheme.sliderTheme.copyWith( | |
trackHeight: effectiveTrackHeight, | |
rangeThumbShape: ShadSliderThumbShape( | |
radius: effectiveThumbRadius, | |
borderColor: effectiveThumbBorderColor, | |
disabledBorderColor: effectiveDisabledThumbBorderColor, | |
thumbColor: effectiveThumbColor, | |
disabledThumbColor: effectiveDisabledThumbColor, | |
), | |
overlayShape: SliderComponentShape.noOverlay, | |
activeTrackColor: effectiveActiveTrackColor, | |
disabledActiveTrackColor: effectiveDisabledActiveTrackColor, | |
inactiveTrackColor: effectiveInactiveTrackColor, | |
disabledInactiveTrackColor: effectiveDisabledInactiveTrackColor, | |
disabledThumbColor: effectiveDisabledThumbColor, | |
), | |
), | |
child: ValueListenableBuilder( | |
valueListenable: controller, | |
builder: (context, value, child) { | |
print(value); | |
return RangeSlider( | |
values: value, | |
min: effectiveMin, | |
max: effectiveMax, | |
mouseCursor: MaterialStateProperty.all( | |
widget.enabled | |
? effectiveMouseCursor | |
: effectiveDisabledMouseCursor, | |
), | |
onChanged: widget.enabled | |
? (v) => () { | |
controller.value = v; | |
} | |
: null, | |
onChangeStart: (v) => widget.onChangeStart!(v.start), | |
onChangeEnd: (v) => widget.onChangeEnd!(v.end), | |
divisions: widget.divisions, | |
semanticFormatterCallback: widget.semanticFormatterCallback, | |
); | |
}, | |
), | |
); | |
} | |
} | |
class ShadSliderThumbShape extends RangeSliderThumbShape { | |
const ShadSliderThumbShape({ | |
required this.radius, | |
required this.borderColor, | |
required this.disabledBorderColor, | |
required this.thumbColor, | |
required this.disabledThumbColor, | |
}); | |
final double radius; | |
final Color borderColor; | |
final Color disabledBorderColor; | |
final Color thumbColor; | |
final Color disabledThumbColor; | |
@override | |
Size getPreferredSize(bool isEnabled, bool isDiscrete) => | |
Size.fromRadius(radius); | |
@override | |
void paint( | |
PaintingContext context, | |
Offset center, { | |
required Animation<double> activationAnimation, | |
required Animation<double> enableAnimation, | |
required SliderThemeData sliderTheme, | |
bool isDiscrete = false, | |
bool isEnabled = true, | |
bool isOnTop = false, | |
bool isPressed = false, | |
TextDirection textDirection = TextDirection.ltr, | |
Thumb thumb = Thumb.start, | |
}) { | |
final canvas = context.canvas; | |
final colorTween = ColorTween( | |
begin: disabledThumbColor, | |
end: thumbColor, | |
); | |
final color = colorTween.evaluate(enableAnimation)!; | |
canvas.drawCircle( | |
center, | |
radius, | |
Paint()..color = color, | |
); | |
final borderColorTween = ColorTween( | |
begin: disabledBorderColor, | |
end: borderColor, | |
); | |
final effectiveBorderColor = borderColorTween.evaluate(enableAnimation)!; | |
final paintBorder = Paint() | |
..color = effectiveBorderColor | |
..strokeWidth = 2 | |
..style = PaintingStyle.stroke; | |
canvas.drawCircle(center, radius, paintBorder); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment