I am trying to create a Range Slider with a gradient body.
I have based my code on a Gradient Slider code from this response: Flutter slider with gradient
I adjusted the Paint override depending on the Interface of the object.
So this is the situation:
Error
======== Exception caught by rendering library =====================================================
The following _CastError was thrown during paint():
Null check operator used on a null value
The relevant error-causing widget was:
RangeSlider RangeSlider:file:///[PATH]flutter/lib/source/shared_components/common/slider_range/slider_range.item.dart:19:14
When the exception was thrown, this was the stack:
#0 BaseSliderTrackShape.getPreferredRect (package:flutter/src/material/slider_theme.dart:1484:53)
#1 _RenderRangeSlider.paint (package:flutter/src/material/range_slider.dart:1267:58)
#2 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#3 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#4 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#5 PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:387:12)
#6 RenderLeaderLayer.paint (package:flutter/src/rendering/proxy_box.dart:5138:13)
#7 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#8 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#9 RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2844:15)
#10 RenderFlex.paint (package:flutter/src/rendering/flex.dart:1078:7)
#11 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#12 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#13 RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#14 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#15 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#16 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#17 RenderDecoratedBox.paint (package:flutter/src/rendering/proxy_box.dart:2169:11)
#18 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#19 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#20 RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#21 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#22 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#23 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#24 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#25 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#26 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#27 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#28 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#29 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#30 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#31 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#32 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#33 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#34 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#35 RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2844:15)
#36 RenderFlex.paint (package:flutter/src/rendering/flex.dart:1078:7)
#37 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#38 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#39 RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#40 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#41 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#42 _RenderSingleChildViewport.paint.paintContents (package:flutter/src/widgets/single_child_scroll_view.dart:542:17)
#43 _RenderSingleChildViewport.paint (package:flutter/src/widgets/single_child_scroll_view.dart:556:9)
#44 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#45 PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:141:11)
#46 PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:100:5)
#47 PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:995:29)
#48 RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:499:19)
#49 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:883:13)
#50 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#51 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#52 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#53 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
#57 _invoke (dart:ui/hooks.dart:151:10)
#58 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#59 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
The following RenderObject was being processed when the exception was fired: _RenderRangeSlider#59316
... parentData: <none> (can use size)
... constraints: BoxConstraints(0.0<=w<=367.4, 0.0<=h<=Infinity)
... semantic boundary
... size: Size(367.4, 48.0)
RenderObject: _RenderRangeSlider#59316
parentData: <none> (can use size)
constraints: BoxConstraints(0.0<=w<=367.4, 0.0<=h<=Infinity)
semantic boundary
size: Size(367.4, 48.0)
====================================================================================================
Track
import 'package:flutter/material.dart';
class GradientRectRangeSliderTrackShape extends RangeSliderTrackShape
with BaseSliderTrackShape {
const GradientRectRangeSliderTrackShape({
this.gradient = const LinearGradient(
colors: [
Colors.red,
Colors.yellow,
],
),
this.darkenInactive = true,
});
final LinearGradient gradient;
final bool darkenInactive;
@override
void paint(
PaintingContext context,
Offset offset,
{
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required TextDirection textDirection,
required Offset startThumbCenter,
required Offset endThumbCenter,
bool isDiscrete = false,
bool isEnabled = false,
double additionalActiveTrackHeight = 2,
}
) {
assert(
sliderTheme.disabledActiveTrackColor != null,
'sliderTheme.disabledActiveTrackColor is required'
);
assert(
sliderTheme.disabledInactiveTrackColor != null,
'sliderTheme.disabledInactiveTrackColor is required'
);
assert(
sliderTheme.activeTrackColor != null,
'sliderTheme.activeTrackColor is required'
);
assert(
sliderTheme.inactiveTrackColor != null,
'sliderTheme.inactiveTrackColor'
);
assert(
sliderTheme.thumbShape != null,
'sliderTheme.thumbShape is required'
);
assert(
sliderTheme.trackHeight != null && sliderTheme.trackHeight! > 0,
'sliderTheme.trackHeight != null and sliderTheme.trackHeight! > 0'
'are required'
);
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
final activeGradientRect = Rect.fromLTRB(
startThumbCenter.dx,
(textDirection == TextDirection.ltr)
? trackRect.top - (additionalActiveTrackHeight / 2)
: trackRect.top,
endThumbCenter.dx,
(textDirection == TextDirection.ltr)
? trackRect.bottom + (additionalActiveTrackHeight / 2)
: trackRect.bottom,
);
// Assign the track segment paints, which are leading: active and
// trailing: inactive.
final ColorTween activeTrackColorTween = ColorTween(
begin: sliderTheme.disabledActiveTrackColor,
end: sliderTheme.activeTrackColor);
final ColorTween inactiveTrackColorTween = darkenInactive
? ColorTween(
begin: sliderTheme.disabledInactiveTrackColor,
end: sliderTheme.inactiveTrackColor
)
: activeTrackColorTween;
final Paint activePaint = Paint()
..shader = gradient.createShader(activeGradientRect)
..color = activeTrackColorTween.evaluate(enableAnimation)!;
final Paint inactivePaint = Paint()
..color = inactiveTrackColorTween.evaluate(enableAnimation)!;
final Paint leftTrackPaint;
final Paint rightTrackPaint;
switch (textDirection) {
case TextDirection.ltr:
leftTrackPaint = activePaint;
rightTrackPaint = inactivePaint;
break;
case TextDirection.rtl:
leftTrackPaint = inactivePaint;
rightTrackPaint = activePaint;
break;
}
final Radius trackRadius = Radius.circular(trackRect.height / 2);
final Radius activeTrackRadius = Radius.circular(trackRect.height / 2 + 1);
context.canvas.drawRRect(
RRect.fromLTRBAndCorners(
startThumbCenter.dx,
(textDirection == TextDirection.ltr)
? trackRect.top - (additionalActiveTrackHeight / 2)
: trackRect.top,
endThumbCenter.dx,
(textDirection == TextDirection.ltr)
? trackRect.bottom + (additionalActiveTrackHeight / 2)
: trackRect.bottom,
topLeft: (textDirection == TextDirection.ltr)
? activeTrackRadius
: trackRadius,
bottomLeft: (textDirection == TextDirection.ltr)
? activeTrackRadius
: trackRadius,
),
leftTrackPaint,
);
context.canvas.drawRRect(
RRect.fromLTRBAndCorners(
startThumbCenter.dx,
(textDirection == TextDirection.rtl)
? trackRect.top - (additionalActiveTrackHeight / 2)
: trackRect.top,
endThumbCenter.dx,
(textDirection == TextDirection.rtl)
? trackRect.bottom + (additionalActiveTrackHeight / 2)
: trackRect.bottom,
topRight: (textDirection == TextDirection.rtl)
? activeTrackRadius
: trackRadius,
bottomRight: (textDirection == TextDirection.rtl)
? activeTrackRadius
: trackRadius,
),
rightTrackPaint,
);
}
}
Widget
SliderTheme(
data: SliderThemeData(
rangeTrackShape: GradientRectRangeSliderTrackShape()
),
child: RangeSlider(
onChanged: onChanged,
values: data.values,
min: data.min,
max: data.max,
divisions: data.labels.length
)
)
This is the code to create a gradient slider range
Track It can be put directly in the Theme
of your app
import 'package:flutter/material.dart';
import 'dart:math' as math;
class GradientRectRangeSliderTrackShape extends RangeSliderTrackShape {
const GradientRectRangeSliderTrackShape({
this.gradient = const LinearGradient(
colors: [
Colors.red,
Colors.yellow,
],
),
this.darkenInactive = true,
});
final LinearGradient gradient;
final bool darkenInactive;
@override
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
assert(
sliderTheme.overlayShape != null,
'sliderTheme.overlayShape is required'
);
assert(
sliderTheme.trackHeight != null,
'sliderTheme.trackHeight is required'
);
final double overlayWidth = sliderTheme.overlayShape!
.getPreferredSize(isEnabled, isDiscrete).width;
final double trackHeight = sliderTheme.trackHeight!;
assert(overlayWidth >= 0);
assert(trackHeight >= 0);
final double trackLeft = offset.dx + overlayWidth / 2;
final double trackTop = offset.dy
+ (parentBox.size.height - trackHeight) / 2;
final double trackRight = trackLeft + parentBox.size.width - overlayWidth;
final double trackBottom = trackTop + trackHeight;
return Rect.fromLTRB(
math.min(
trackLeft,
trackRight
),
trackTop,
math.max(
trackLeft,
trackRight
),
trackBottom
);
}
@override
void paint(
PaintingContext context,
Offset offset, {
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required Offset startThumbCenter,
required Offset endThumbCenter,
bool isEnabled = false,
bool isDiscrete = false,
required TextDirection textDirection,
double additionalActiveTrackHeight = 2,
}) {
assert(
sliderTheme.disabledActiveTrackColor != null,
'sliderTheme.disabledActiveTrackColor is required'
);
assert(
sliderTheme.disabledInactiveTrackColor != null,
'sliderTheme.disabledInactiveTrackColor is required'
);
assert(
sliderTheme.activeTrackColor != null,
'sliderTheme.activeTrackColor is required'
);
assert(
sliderTheme.inactiveTrackColor != null,
'sliderTheme.inactiveTrackColor is required'
);
assert(
sliderTheme.rangeThumbShape != null,
'sliderTheme.rangeThumbShape iss required'
);
assert(
sliderTheme.trackHeight != null && sliderTheme.trackHeight! > 0,
'sliderTheme.trackHeight != null and sliderTheme.trackHeight! > 0'
'are required'
);
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
final ColorTween activeTrackColorTween = ColorTween(
begin: sliderTheme.disabledActiveTrackColor,
end: sliderTheme.activeTrackColor,
);
final ColorTween inactiveTrackColorTween = darkenInactive
? ColorTween(
begin: sliderTheme.disabledInactiveTrackColor,
end: sliderTheme.inactiveTrackColor,
)
: activeTrackColorTween;
final Paint activePaint = Paint()
..shader = gradient.createShader(trackRect)
..color = activeTrackColorTween.evaluate(enableAnimation)!;
final Paint inactivePaint = Paint()
..color = inactiveTrackColorTween.evaluate(enableAnimation)!;
final Offset leftThumbOffset;
final Offset rightThumbOffset;
switch (textDirection) {
case TextDirection.ltr:
leftThumbOffset = startThumbCenter;
rightThumbOffset = endThumbCenter;
break;
case TextDirection.rtl:
leftThumbOffset = endThumbCenter;
rightThumbOffset = startThumbCenter;
break;
}
final Size thumbSize = sliderTheme.rangeThumbShape!
.getPreferredSize(
isEnabled,
isDiscrete
);
final double thumbRadius = thumbSize.width / 2;
assert(thumbRadius > 0);
final Radius trackRadius = Radius.circular(trackRect.height / 2);
context.canvas.drawRRect(
RRect.fromLTRBAndCorners(
trackRect.left,
trackRect.top,
leftThumbOffset.dx,
trackRect.bottom,
topLeft: trackRadius,
bottomLeft: trackRadius,
),
inactivePaint,
);
context.canvas.drawRect(
Rect.fromLTRB(
leftThumbOffset.dx,
trackRect.top - (additionalActiveTrackHeight / 2),
rightThumbOffset.dx,
trackRect.bottom + (additionalActiveTrackHeight / 2),
),
activePaint,
);
context.canvas.drawRRect(
RRect.fromLTRBAndCorners(
rightThumbOffset.dx,
trackRect.top,
trackRect.right,
trackRect.bottom,
topRight: trackRadius,
bottomRight: trackRadius,
),
inactivePaint,
);
}
}
Widget
SliderTheme(
data: SliderThemeData(
rangeTrackShape: GradientRectRangeSliderTrackShape()
),
child: RangeSlider(
onChanged: onChanged,
values: data.values,
min: data.min,
max: data.max,
divisions: data.labels.length
)
)