我正在使用基本的滑块,并且发现了如何仅更新想要更改的滑块主题数据的部分(例如trackHeight),但是不幸的是,我不确定如何更新“ trackShape”的字段。例如,这是我在主应用程序中执行的以更新轨道高度的操作:
final SliderThemeData tweakedSliderTheme = Theme.of(context).sliderTheme.copyWith(
trackHeight: 22.0,
//trackShape: RectangularSliderTrackShape(), // How do I update this??
);
我确实尝试在滑块窗口小部件周围使用ClipRRect(),但没有任何效果。
这是一个滑块的简单页面:
import 'package:flutter/material.dart';
class RoomControl extends StatefulWidget {
@override
_RoomControlState createState() => _RoomControlState();
}
class _RoomControlState extends State<RoomControl> {
double _value = 0.0;
void _setvalue(double value) => setState(() => _value = value);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Name here'),
),
//hit Ctrl+space in intellij to know what are the options you can use in flutter widgets
body: Container(
padding: EdgeInsets.all(32.0),
child: Center(
child: Column(
children: <Widget>[
Text('Value: ${(_value * 100).round()}'),
ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child:Slider(
value: _value,
onChanged: _setvalue,
divisions: 10,
)
)
],
),
),
),
);
}
}
以下是该滑块的外观:
更新: 得到答案后,我可以通过更新刻度线形状和拇指形状来轻松创建如下内容:
final SliderThemeData tweakedSliderTheme = Theme.of(context).sliderTheme.copyWith(
trackHeight: 20.0,
thumbShape: MyRoundSliderThumbShape(enabledThumbRadius: 13.0, disabledThumbRadius: 13.0),
trackShape: MyRoundSliderTrackShape(), // TODO: this is hard coded right now for 20 track height
inactiveTrackColor: lightGreySliderColor,
activeTickMarkColor: Color(blackPrimaryValue),
inactiveTickMarkColor: colorizedMenuColor,
tickMarkShape: MyRectSliderTickMarkShape(tickMarkRadius: 4.0),
);
刻度线形状有些技巧。如果将其设置得太大,它会跳过绘画!也许是有道理的,但我对绘画/渲染并不了解,所以花了我一段时间来学习如何使刻度线(Rect)正确显示
答案 0 :(得分:2)
我已将基础代码从RectangularSliderTrackShape
复制到一个名为RoundSliderTrackShape
的新类中。
round_slider_track_shape.dart
import 'dart:math';
import 'package:flutter/material.dart';
class RoundSliderTrackShape extends SliderTrackShape {
/// Create a slider track that draws 2 rectangles.
const RoundSliderTrackShape({ this.disabledThumbGapWidth = 2.0 });
/// Horizontal spacing, or gap, between the disabled thumb and the track.
///
/// This is only used when the slider is disabled. There is no gap around
/// the thumb and any part of the track when the slider is enabled. The
/// Material spec defaults this gap width 2, which is half of the disabled
/// thumb radius.
final double disabledThumbGapWidth;
@override
Rect getPreferredRect({
RenderBox parentBox,
Offset offset = Offset.zero,
SliderThemeData sliderTheme,
bool isEnabled,
bool isDiscrete,
}) {
final double overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width;
final double trackHeight = sliderTheme.trackHeight;
assert(overlayWidth >= 0);
assert(trackHeight >= 0);
assert(parentBox.size.width >= overlayWidth);
assert(parentBox.size.height >= trackHeight);
final double trackLeft = offset.dx + overlayWidth / 2;
final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
// TODO(clocksmith): Although this works for a material, perhaps the default
// rectangular track should be padded not just by the overlay, but by the
// max of the thumb and the overlay, in case there is no overlay.
final double trackWidth = parentBox.size.width - overlayWidth;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
@override
void paint(
PaintingContext context,
Offset offset, {
RenderBox parentBox,
SliderThemeData sliderTheme,
Animation<double> enableAnimation,
TextDirection textDirection,
Offset thumbCenter,
bool isDiscrete,
bool isEnabled,
}) {
// If the slider track height is 0, then it makes no difference whether the
// track is painted or not, therefore the painting can be a no-op.
if (sliderTheme.trackHeight == 0) {
return;
}
// Assign the track segment paints, which are left: active, right: inactive,
// but reversed for right to left text.
final ColorTween activeTrackColorTween = ColorTween(begin: sliderTheme.disabledActiveTrackColor , end: sliderTheme.activeTrackColor);
final ColorTween inactiveTrackColorTween = ColorTween(begin: sliderTheme.disabledInactiveTrackColor , end: sliderTheme.inactiveTrackColor);
final Paint activePaint = Paint()..color = activeTrackColorTween.evaluate(enableAnimation);
final Paint inactivePaint = Paint()..color = inactiveTrackColorTween.evaluate(enableAnimation);
Paint leftTrackPaint;
Paint rightTrackPaint;
switch (textDirection) {
case TextDirection.ltr:
leftTrackPaint = activePaint;
rightTrackPaint = inactivePaint;
break;
case TextDirection.rtl:
leftTrackPaint = inactivePaint;
rightTrackPaint = activePaint;
break;
}
// Used to create a gap around the thumb iff the slider is disabled.
// If the slider is enabled, the track can be drawn beneath the thumb
// without a gap. But when the slider is disabled, the track is shortened
// and this gap helps determine how much shorter it should be.
// TODO(clocksmith): The new Material spec has a gray circle in place of this gap.
double horizontalAdjustment = 0.0;
if (!isEnabled) {
final double disabledThumbRadius = sliderTheme.thumbShape.getPreferredSize(false, isDiscrete).width / 2.0;
final double gap = disabledThumbGapWidth * (1.0 - enableAnimation.value);
horizontalAdjustment = disabledThumbRadius + gap;
}
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
final Rect leftTrackSegment = Rect.fromLTRB(trackRect.left, trackRect.top, thumbCenter.dx - horizontalAdjustment, trackRect.bottom);
// Left Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.left, trackRect.top + 11.0), radius: 11.0),
-pi * 3 / 2, // -270 degrees
pi, // 180 degrees
false,
trackRect.left - thumbCenter.dx == 0.0 ? rightTrackPaint : leftTrackPaint
);
// Right Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.right, trackRect.top + 11.0), radius: 11.0),
-pi / 2, // -90 degrees
pi, // 180 degrees
false,
trackRect.right - thumbCenter.dx == 0.0 ? leftTrackPaint : rightTrackPaint
);
context.canvas.drawRect(leftTrackSegment, leftTrackPaint);
final Rect rightTrackSegment = Rect.fromLTRB(thumbCenter.dx + horizontalAdjustment, trackRect.top, trackRect.right, trackRect.bottom);
context.canvas.drawRect(rightTrackSegment, rightTrackPaint);
}
}
以下是SliderTheme的设置方式。
import 'package:flutter_stackoverflow/round_slider_track_shape.dart';
...
sliderTheme: Theme.of(context).sliderTheme.copyWith(
trackHeight: 22.0,
trackShape: RoundSliderTrackShape(),
activeTrackColor: Colors.green,
// trackShape: RectangularSliderTrackShape(),
),
添加的是SliderTrack侧面的两个圆弧
// Left Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.left, trackRect.top + 11.0), radius: 11.0),
-pi * 3 / 2, // -270 degrees
pi, // 180 degrees
false,
trackRect.left - thumbCenter.dx == 0.0 ? rightTrackPaint : leftTrackPaint
);
// Right Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.right, trackRect.top + 11.0), radius: 11.0),
-pi / 2, // -90 degrees
pi, // 180 degrees
false,
trackRect.right - thumbCenter.dx == 0.0 ? leftTrackPaint : rightTrackPaint
);
答案 1 :(得分:1)
滑块的默认trackShape
现在是母版中的RoundedRectSliderTrackShape
:
对您有用吗?
可以通过覆盖SliderThemeData中的RectangularSliderTrackShape
来保留 trackShape
答案 2 :(得分:1)
@Jun Xiang的回答似乎有效,但是我对以下代码进行了较小的更改:
// Left Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.left, trackRect.top + 11.0), radius: 11.0),
-pi * 3 / 2, // -270 degrees
pi, // 180 degrees
false,
trackRect.left - thumbCenter.dx == 0.0 ? rightTrackPaint : leftTrackPaint
);
// Right Arc
context.canvas.drawArc(
Rect.fromCircle(center: Offset(trackRect.right, trackRect.top + 11.0), radius: 11.0),
-pi / 2, // -90 degrees
pi, // 180 degrees
false,
trackRect.right - thumbCenter.dx == 0.0 ? leftTrackPaint : rightTrackPaint
);
我使用了 sliderTheme.trackHeight * 1/2 ,而不是使用 11.0 ,并且似乎适用于所有输入的轨道高度(不仅是22)。
PS:我无法发表评论,所以我发布了答案。
答案 3 :(得分:0)
我有一个更好的解决方案
import 'package:flutter/material.dart';
class RoundSliderTrackShape extends SliderTrackShape {
const RoundSliderTrackShape({this.disabledThumbGapWidth = 2.0, this.radius = 0});
final double disabledThumbGapWidth;
final double radius;
@override
Rect getPreferredRect({
RenderBox parentBox,
Offset offset = Offset.zero,
SliderThemeData sliderTheme,
bool isEnabled,
bool isDiscrete,
}) {
final double overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width;
final double trackHeight = sliderTheme.trackHeight;
assert(overlayWidth >= 0);
assert(trackHeight >= 0);
assert(parentBox.size.width >= overlayWidth);
assert(parentBox.size.height >= trackHeight);
final double trackLeft = offset.dx + overlayWidth / 2;
final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width - overlayWidth;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
@override
void paint(
PaintingContext context,
Offset offset, {
RenderBox parentBox,
SliderThemeData sliderTheme,
Animation<double> enableAnimation,
TextDirection textDirection,
Offset thumbCenter,
bool isDiscrete,
bool isEnabled,
}) {
if (sliderTheme.trackHeight == 0) {
return;
}
final ColorTween activeTrackColorTween =
ColorTween(begin: sliderTheme.disabledActiveTrackColor, end: sliderTheme.activeTrackColor);
final ColorTween inactiveTrackColorTween =
ColorTween(begin: sliderTheme.disabledInactiveTrackColor, end: sliderTheme.inactiveTrackColor);
final Paint activePaint = Paint()..color = activeTrackColorTween.evaluate(enableAnimation);
final Paint inactivePaint = Paint()..color = inactiveTrackColorTween.evaluate(enableAnimation);
Paint leftTrackPaint;
Paint rightTrackPaint;
switch (textDirection) {
case TextDirection.ltr:
leftTrackPaint = activePaint;
rightTrackPaint = inactivePaint;
break;
case TextDirection.rtl:
leftTrackPaint = inactivePaint;
rightTrackPaint = activePaint;
break;
}
double horizontalAdjustment = 0.0;
if (!isEnabled) {
final double disabledThumbRadius =
sliderTheme.thumbShape.getPreferredSize(false, isDiscrete).width / 2.0;
final double gap = disabledThumbGapWidth * (1.0 - enableAnimation.value);
horizontalAdjustment = disabledThumbRadius + gap;
}
final Rect trackRect = getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete,
);
//Modify this side
final RRect leftTrackSegment = RRect.fromLTRBR(trackRect.left, trackRect.top,
thumbCenter.dx - horizontalAdjustment, trackRect.bottom, Radius.circular(radius));
context.canvas.drawRRect(leftTrackSegment, leftTrackPaint);
final RRect rightTrackSegment = RRect.fromLTRBR(thumbCenter.dx + horizontalAdjustment, trackRect.top,
trackRect.right, trackRect.bottom, Radius.circular(radius));
context.canvas.drawRRect(rightTrackSegment, rightTrackPaint);
}
}
使用:
trackShape: RoundSliderTrackShape(radius: 8)
答案 4 :(得分:0)
创建这样的自定义形状,在这个过程中,我在拇指上绘制了2个圆圈
class SliderThumbShape extends SliderComponentShape {
/// Create a slider thumb that draws a circle.
const SliderThumbShape({
this.enabledThumbRadius = 10.0,
this.disabledThumbRadius,
this.elevation = 1.0,
this.pressedElevation = 6.0,
});
/// The preferred radius of the round thumb shape when the slider is enabled.
///
/// If it is not provided, then the material default of 10 is used.
final double enabledThumbRadius;
/// The preferred radius of the round thumb shape when the slider is disabled.
///
/// If no disabledRadius is provided, then it is equal to the
/// [enabledThumbRadius]
final double disabledThumbRadius;
double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
/// The resting elevation adds shadow to the unpressed thumb.
///
/// The default is 1.
///
/// Use 0 for no shadow. The higher the value, the larger the shadow. For
/// example, a value of 12 will create a very large shadow.
///
final double elevation;
/// The pressed elevation adds shadow to the pressed thumb.
///
/// The default is 6.
///
/// Use 0 for no shadow. The higher the value, the larger the shadow. For
/// example, a value of 12 will create a very large shadow.
final double pressedElevation;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(isEnabled == true ? enabledThumbRadius : _disabledThumbRadius);
}
@override
void paint(
PaintingContext context,
Offset center, {
Animation<double> activationAnimation,
@required Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
@required SliderThemeData sliderTheme,
TextDirection textDirection,
double value,
double textScaleFactor,
Size sizeWithOverflow,
}) {
assert(context != null);
assert(center != null);
assert(enableAnimation != null);
assert(sliderTheme != null);
assert(sliderTheme.disabledThumbColor != null);
assert(sliderTheme.thumbColor != null);
assert(!sizeWithOverflow.isEmpty);
final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>(
begin: _disabledThumbRadius,
end: enabledThumbRadius,
);
final double radius = radiusTween.evaluate(enableAnimation);
final Tween<double> elevationTween = Tween<double>(
begin: elevation,
end: pressedElevation,
);
final double evaluatedElevation = elevationTween.evaluate(activationAnimation);
{
final Path path = Path()
..addArc(Rect.fromCenter(center: center, width: 1 * radius, height: 1 * radius), 0, math.pi * 2);
Paint paint = Paint()..color = Colors.black;
paint.strokeWidth = 15;
paint.style = PaintingStyle.stroke;
canvas.drawCircle(
center,
radius,
paint,
);
{
Paint paint = Paint()..color = Colors.white;
paint.style = PaintingStyle.fill;
canvas.drawCircle(
center,
radius,
paint,
);
}
}
}
}
然后像这样在您的小部件树中使用它
SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.blue,
inactiveTrackColor: Color(0xffd0d2d3),
trackHeight: 2,
thumbShape: SliderThumbShape(),
),
child: Slider(
onChanged: (value) {},
value: 40.5,
max: 100,
min: 0,
),
),
答案 5 :(得分:0)
另一种选择是用圆形 StrokeCap
画一条粗线。两端的帽半径加上线宽。
@override
void paint(PaintingContext context, Offset offset,
{required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required Offset thumbCenter,
bool isEnabled = true,
bool isDiscrete = true,
required TextDirection textDirection}) {
final canvas = context.canvas;
final width = 200;
final height = 16;
canvas.drawLine(
Offset(0, height / 2),
Offset(width, height / 2),
Paint()
..strokeCap = StrokeCap.round
..strokeWidth = height);
}