我正在尝试创建一个带有渐变主体的范围滑块。
我的代码基于此响应中的渐变滑块代码:带有渐变的颤动滑块
我根据对象的界面调整了绘制覆盖。
所以情况是这样的:
错误
======== 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)
====================================================================================================
追踪
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,
);
}
}
小工具
SliderTheme(
data: SliderThemeData(
rangeTrackShape: GradientRectRangeSliderTrackShape()
),
child: RangeSlider(
onChanged: onChanged,
values: data.values,
min: data.min,
max: data.max,
divisions: data.labels.length
)
)
这是创建渐变滑块范围的代码
Track 可以直接放在您应用程序的
Theme
中
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,
);
}
}
小工具
SliderTheme(
data: SliderThemeData(
rangeTrackShape: GradientRectRangeSliderTrackShape()
),
child: RangeSlider(
onChanged: onChanged,
values: data.values,
min: data.min,
max: data.max,
divisions: data.labels.length
)
)
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gradient Range Slider Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Gradient Range Slider Demo'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: GradientRangeSlider(),
),
);
}
}
class GradientRangeSlider extends StatefulWidget {
@override
_GradientRangeSliderState createState() => _GradientRangeSliderState();
}
class _GradientRangeSliderState extends State<GradientRangeSlider> {
RangeValues _currentRangeValues = const RangeValues(20, 80);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Volume Control',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
SliderTheme(
data: SliderTheme.of(context).copyWith(
rangeTrackShape: GradientRectRangeSliderTrackShape(
gradient: LinearGradient(
colors: [Colors.purple, Colors.pink, Colors.yellow],
),
),
activeTrackColor: Colors.transparent,
inactiveTrackColor: Colors.grey.withOpacity(0.5),
thumbColor: Colors.green,
overlayColor: Colors.blue.withOpacity(0.3),
valueIndicatorColor: Colors.blue,
activeTickMarkColor: Colors.white,
inactiveTickMarkColor: Colors.white.withOpacity(0.7),
),
child: RangeSlider(
values: _currentRangeValues,
min: 0,
max: 100,
divisions: 100,
labels: RangeLabels(
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString(),
),
onChanged: (RangeValues values) {
setState(() {
_currentRangeValues = values;
});
},
),
),
SizedBox(height: 20),
Text(
'Volume Range: ${_currentRangeValues.start.round()} - ${_currentRangeValues.end.round()}',
style: TextStyle(fontSize: 16),
),
],
);
}
}
class GradientRectRangeSliderTrackShape
extends RectangularRangeSliderTrackShape {
final Gradient gradient;
const GradientRectRangeSliderTrackShape({required this.gradient});
@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 isEnabled = false,
bool isDiscrete = false,
double additionalActiveTrackHeight = 2,
}) {
if (!isEnabled) {
super.paint(
context,
offset,
parentBox: parentBox,
sliderTheme: sliderTheme,
enableAnimation: enableAnimation,
textDirection: textDirection,
startThumbCenter: startThumbCenter,
endThumbCenter: endThumbCenter,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
return;
}
final Canvas canvas = context.canvas;
final Paint paint = Paint()
..shader = gradient.createShader(
Rect.fromLTWH(0, 0, parentBox.size.width, parentBox.size.height));
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
final Rect activeTrackRect = Rect.fromLTRB(
startThumbCenter.dx,
trackRect.top,
endThumbCenter.dx,
trackRect.bottom,
);
canvas.drawRect(activeTrackRect, paint);
}
}