Flutter 里的 Slider 組件類似 Android 里的 SeekBar 組件,中間有一個滑塊,可以滑動選擇進度。最近在使用 Slider 組件時確碰到一個問題,具體如下圖所示:

圖中可以看到,組件上面有一個個等分的點,剛開始還以為是什么屬性配置錯誤了,后來通過研究官方文檔以及源碼發(fā)現(xiàn),這叫tickMark,俗稱刻度。但是我只想要一個單純的滑動條,并不想要在上面顯示刻度,找了一圈發(fā)現(xiàn) Slider 并沒有屬性可以控制這個。
后來經(jīng)過一番查找,發(fā)現(xiàn)可以通過 SliderTheme 來改變 Slider 組件的一些默認屬性,里面恰好有 activeTickMarkColor、inactiveTickMarkColor、tickMarkShape是與tickMark相關(guān)的。于是想到 將 tickMarkColor 配置成透明色不就不顯示刻度了么,試驗了一下如下代碼:
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTickMarkColor: Colors.transparent,
inactiveTickMarkColor: Colors.transparent
),
child: Slider(
min: 1, max: 100, value: _value, divisions: 10,
activeColor: Colors.red,
inactiveColor: Colors.grey,
onChanged: (value) {
setState(() {
_value = value;
});
}));
結(jié)果很遺憾,一點效果都沒有(有知道原因的可以共享下),無論設(shè)置成啥顏色都不起作用。
于是,目光只能投向tickMarkShape,看描述是來繪制tickMark形狀的,其類型為SliderTickMarkShape,默認的實現(xiàn)有RoundSliderTickMarkShape,看其源碼其核心就有一個paint 方法,里面就是用來繪制tickMark的:
@override
void paint(
PaintingContext context,
Offset center, {
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required TextDirection textDirection,
required Offset thumbCenter,
required bool isEnabled,
}) {
assert(context != null);
assert(center != null);
assert(parentBox != null);
assert(sliderTheme != null);
assert(sliderTheme.disabledActiveTickMarkColor != null);
assert(sliderTheme.disabledInactiveTickMarkColor != null);
assert(sliderTheme.activeTickMarkColor != null);
assert(sliderTheme.inactiveTickMarkColor != null);
assert(enableAnimation != null);
assert(textDirection != null);
assert(thumbCenter != null);
assert(isEnabled != null);
// The paint color of the tick mark depends on its position relative
// to the thumb and the text direction.
Color? begin;
Color? end;
switch (textDirection) {
case TextDirection.ltr:
final bool isTickMarkRightOfThumb = center.dx > thumbCenter.dx;
begin = isTickMarkRightOfThumb ? sliderTheme.disabledInactiveTickMarkColor : sliderTheme.disabledActiveTickMarkColor;
end = isTickMarkRightOfThumb ? sliderTheme.inactiveTickMarkColor : sliderTheme.activeTickMarkColor;
break;
case TextDirection.rtl:
final bool isTickMarkLeftOfThumb = center.dx < thumbCenter.dx;
begin = isTickMarkLeftOfThumb ? sliderTheme.disabledInactiveTickMarkColor : sliderTheme.disabledActiveTickMarkColor;
end = isTickMarkLeftOfThumb ? sliderTheme.inactiveTickMarkColor : sliderTheme.activeTickMarkColor;
break;
}
final Paint paint = Paint()..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!;
// The tick marks are tiny circles that are the same height as the track.
final double tickMarkRadius = getPreferredSize(
isEnabled: isEnabled,
sliderTheme: sliderTheme,
).width / 2;
if (tickMarkRadius > 0) {
context.canvas.drawCircle(center, tickMarkRadius, paint);
}
}
上面可以看到只有當 tickMarkRadius > 0 時,才會繪制一個圓圈,為了不讓顯示tickMark我們只需將tickMarkRadius設(shè)置成 <=0 就可以了,最終設(shè)置如下:
SliderTheme(
data: SliderTheme.of(context).copyWith(
//這里 tickMarkRadius 設(shè)置成 0 即可
tickMarkShape: const RoundSliderTickMarkShape(tickMarkRadius: 0)
),
child: Slider(
min: 1, max: 100, value: _value, divisions: 10,
activeColor: Colors.red,
inactiveColor: Colors.grey,
onChanged: (value) {
setState(() {
_value = value;
});
}));
最終效果如下:

除此外,我們還可以自定義一個類來繼承SliderTickMarkShape,在paint方法里什么都不做,同樣可以達到一樣的效果,代碼如下:
class TransparentSliderTickMarkShape extends SliderTickMarkShape {
@override
Size getPreferredSize({required SliderThemeData sliderTheme, required bool isEnabled}) {
return const Size(0, 0);
}
@override
void paint(PaintingContext context, Offset center, {required RenderBox parentBox, required SliderThemeData sliderTheme, required Animation<double> enableAnimation, required Offset thumbCenter, required bool isEnabled, required TextDirection textDirection}) {
}
}
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTickMarkColor: Colors.transparent,
inactiveTickMarkColor: Colors.transparent,
tickMarkShape: TransparentSliderTickMarkShape()
),
child: Slider(
min: 1, max: 100, value: _value, divisions: 10,
activeColor: Colors.red,
inactiveColor: Colors.grey,
onChanged: (value) {
setState(() {
_value = value;
});
}))