1+ import 'dart:math' as math;
2+
13import 'package:codelessly_api/codelessly_api.dart' ;
24import 'package:flutter/material.dart' ;
35
@@ -94,13 +96,29 @@ class PassiveSliderWidget extends StatelessWidget {
9496 scopedValues: scopedValues,
9597 ) ??
9698 node.value;
99+ final double min =
100+ PropertyValueDelegate .getPropertyValue <double >(
101+ node,
102+ 'min' ,
103+ scopedValues: scopedValues,
104+ ) ??
105+ node.properties.min;
106+ final double max =
107+ PropertyValueDelegate .getPropertyValue <double >(
108+ node,
109+ 'max' ,
110+ scopedValues: scopedValues,
111+ ) ??
112+ node.properties.max;
97113
98114 return TransformerSlider (
99115 node: node,
100116 settings: settings,
101117 variablesOverrides: variablesOverrides,
102118 onChanged: (value) => onChanged (context, value),
103119 value: value,
120+ min: min,
121+ max: max,
104122 );
105123 }
106124}
@@ -113,6 +131,8 @@ class TransformerSlider extends StatelessWidget {
113131 required this .onChanged,
114132 required this .value,
115133 this .variablesOverrides = const [],
134+ this .min,
135+ this .max,
116136 });
117137
118138 final SliderNode node;
@@ -121,8 +141,18 @@ class TransformerSlider extends StatelessWidget {
121141 final ValueChanged <double >? onChanged;
122142 final double value;
123143
144+ /// The slider's minimum, when bound; [SliderProperties.min] otherwise.
145+ final double ? min;
146+
147+ /// The slider's maximum, when bound; [SliderProperties.max] otherwise.
148+ /// Never less than the minimum.
149+ final double ? max;
150+
124151 @override
125152 Widget build (BuildContext context) {
153+ final double min = this .min ?? node.properties.min;
154+ final double max = math.max (min, this .max ?? node.properties.max);
155+ final bool vertical = node.properties.orientation == AxisC .vertical;
126156 final SliderTrackShape trackShape = switch (node.properties.trackShape) {
127157 SliderTrackShapeEnum .rectangle => const RectangularSliderTrackShape (),
128158 SliderTrackShapeEnum .roundedRectangle =>
@@ -158,9 +188,12 @@ class TransformerSlider extends StatelessWidget {
158188 };
159189 final bool showsLabel = showValueIndicator != ShowValueIndicator .never;
160190
161- return SizedBox (
162- width: node.basicBoxLocal.width,
163- height: node.basicBoxLocal.height,
191+ // A vertical slider is a horizontal one turned a quarter turn
192+ // counterclockwise, so min sits at the bottom. RotatedBox turns its
193+ // layout and hit testing too, so the whole node box drags the thumb.
194+ final Widget slider = SizedBox (
195+ width: vertical ? node.basicBoxLocal.height : node.basicBoxLocal.width,
196+ height: vertical ? node.basicBoxLocal.width : node.basicBoxLocal.height,
164197 child: SliderTheme (
165198 data: SliderThemeData (
166199 trackHeight: node.properties.trackHeight,
@@ -171,10 +204,12 @@ class TransformerSlider extends StatelessWidget {
171204 inactiveTrackColor: node.properties.inactiveTrackColor
172205 .toFlutterColor (),
173206 thumbColor: node.properties.thumbColor.toFlutterColor (),
174- thumbShape: RoundSliderThumbShape (
207+ thumbShape: StrokedSliderThumbShape (
175208 enabledThumbRadius: node.properties.showThumb
176209 ? node.properties.thumbRadius
177210 : 0 ,
211+ strokeColor: node.properties.thumbStrokeColor? .toFlutterColor (),
212+ strokeWidth: node.properties.thumbStrokeWidth,
178213 ),
179214 trackShape: trackShape,
180215 // GappedSliderTrackShape requires a gap; 6 is the Material 3
@@ -208,22 +243,100 @@ class TransformerSlider extends StatelessWidget {
208243 ),
209244 ),
210245 child: Slider (
211- value: value.clamp (node.properties. min, node.properties. max),
246+ value: value.clamp (min, max),
212247 onChanged: onChanged,
213248 autofocus: node.properties.autofocus,
214- min: node.properties. min,
215- max: node.properties. max,
249+ min: min,
250+ max: max,
216251 divisions: node.properties.isDiscrete
217252 ? node.properties.divisions
218253 : null ,
219254 label: showsLabel ? getSliderLabel (node, value) : null ,
220255 secondaryTrackValue: node.properties.secondaryTrackValue? .clamp (
221- node.properties. min,
222- node.properties. max,
256+ min,
257+ max,
223258 ),
224259 padding: node.properties.padding? .flutterEdgeInsets,
225260 ),
226261 ),
227262 );
263+
264+ final TextDirection direction =
265+ Directionality .maybeOf (context) ?? TextDirection .ltr;
266+ final TextDirection flipped = direction == TextDirection .ltr
267+ ? TextDirection .rtl
268+ : TextDirection .ltr;
269+ // Vertical runs bottom to top in left to right text, so an inverted
270+ // vertical slider runs top to bottom.
271+ final Widget directed = node.properties.inverted
272+ ? Directionality (textDirection: flipped, child: slider)
273+ : Directionality (textDirection: direction, child: slider);
274+
275+ if (! vertical) {
276+ return node.properties.inverted ? directed : slider;
277+ }
278+ return SizedBox (
279+ width: node.basicBoxLocal.width,
280+ height: node.basicBoxLocal.height,
281+ child: RotatedBox (quarterTurns: 3 , child: directed),
282+ );
283+ }
284+ }
285+
286+ /// A [RoundSliderThumbShape] with an optional stroke drawn inside its edge.
287+ class StrokedSliderThumbShape extends RoundSliderThumbShape {
288+ /// Creates a [StrokedSliderThumbShape] .
289+ const StrokedSliderThumbShape ({
290+ super .enabledThumbRadius,
291+ this .strokeColor,
292+ this .strokeWidth = 0 ,
293+ });
294+
295+ /// Color of the stroke; no stroke when null.
296+ final Color ? strokeColor;
297+
298+ /// Width of the stroke; no stroke when 0.
299+ final double strokeWidth;
300+
301+ @override
302+ void paint (
303+ PaintingContext context,
304+ Offset center, {
305+ required Animation <double > activationAnimation,
306+ required Animation <double > enableAnimation,
307+ required bool isDiscrete,
308+ required TextPainter labelPainter,
309+ required RenderBox parentBox,
310+ required SliderThemeData sliderTheme,
311+ required TextDirection textDirection,
312+ required double value,
313+ required double textScaleFactor,
314+ required Size sizeWithOverflow,
315+ }) {
316+ super .paint (
317+ context,
318+ center,
319+ activationAnimation: activationAnimation,
320+ enableAnimation: enableAnimation,
321+ isDiscrete: isDiscrete,
322+ labelPainter: labelPainter,
323+ parentBox: parentBox,
324+ sliderTheme: sliderTheme,
325+ textDirection: textDirection,
326+ value: value,
327+ textScaleFactor: textScaleFactor,
328+ sizeWithOverflow: sizeWithOverflow,
329+ );
330+ final Color ? color = strokeColor;
331+ if (color == null || strokeWidth <= 0 || enabledThumbRadius <= 0 ) return ;
332+ final double width = math.min (strokeWidth, enabledThumbRadius);
333+ context.canvas.drawCircle (
334+ center,
335+ enabledThumbRadius - width / 2 ,
336+ Paint ()
337+ ..style = PaintingStyle .stroke
338+ ..strokeWidth = width
339+ ..color = color,
340+ );
228341 }
229342}
0 commit comments