Flutter 自定义滑块拇指可选区域与自定义滑块轨迹不正确

Flutter 自定义滑块拇指可选区域与自定义滑块轨迹不正确,flutter,dart,Flutter,Dart,在我的自定义滑块拇指和轨迹中,可以选择滑块拇指的错误区域进行拖动。代码如下: import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold(

在我的自定义滑块拇指和轨迹中,可以选择滑块拇指的错误区域进行拖动。代码如下:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            SizedBox(height: 100),
            Row(children: [
              SizedBox(width: 100),
              CustomSlider()
            ])
          ]
        )
      )
    );
  }
}


class CustomSlider extends StatefulWidget {
  CustomSlider({Key? key}) : super(key: key);

  @override
  _CustomSliderState createState() => _CustomSliderState();
}

class _CustomSliderState extends State<CustomSlider> {
  double _value = .5;

  @override
  Widget build(BuildContext context) {
    return SliderTheme(
        data: SliderThemeData(
          thumbShape: _SliderThumbImage(),
          trackShape: _SliderTrack(), // Commenting results in correct selectable thumb region
        ),
        child: Slider(
          min: .1, max: 1, value: _value,
          onChanged: (value) => setState(() { _value = value; })
        ));
  }
}

class _SliderThumbImage extends SliderComponentShape {
  static const thumbSideLength = 60.0;
  
  @override
  void paint(PaintingContext context, Offset center,
      {required Animation<double> activationAnimation, required Animation<double> enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow}) {
    final canvas = context.canvas;

    Offset thumbDrawOffset = Offset(center.dx - (thumbSideLength / 2), center.dy - (thumbSideLength / 2));

    canvas.drawRect(Rect.fromLTWH(thumbDrawOffset.dx, thumbDrawOffset.dy, thumbSideLength, thumbSideLength),
                    Paint()..color = Colors.black);
  }

  @override
  Size getPreferredSize(bool isEnabled, bool isDiscrete) {
    return Size(thumbSideLength, thumbSideLength);
  }
}

class _SliderTrack extends SliderTrackShape {
  static const width = 184.0;
  static const trackHeight = 16.0;

  @override
  Rect getPreferredRect(
      {required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = true, bool isDiscrete = true}) {
    return Rect.fromLTWH(0, 0, width, trackHeight);
  }

  @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;

    canvas.drawLine(
        Offset(offset.dx, offset.dy + trackHeight / 2),
        Offset(offset.dx + width, offset.dy + trackHeight / 2),
        Paint()
          ..color = Colors.amber
          ..strokeCap = StrokeCap.round
          ..strokeWidth = trackHeight);
  }
}
导入“包装:颤振/材料.省道”;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
家:脚手架(
正文:专栏(
儿童:[
尺寸箱(高度:100),
世界其他地区(儿童:[
尺寸箱(宽度:100),
自定义滑块()
])
]
)
)
);
}
}
类CustomSlider扩展StatefulWidget{
CustomSlider({Key?Key}):超级(Key:Key);
@凌驾
_CustomSliderState createState()=>\u CustomSliderState();
}
类_CustomSliderState扩展状态{
双_值=.5;
@凌驾
小部件构建(构建上下文){
返回滑块主题(
数据:SliderThemeData(
拇指形状:_SliderThumbImage(),
trackShape:_SliderTrack(),//注释结果为正确的可选择拇指区域
),
子:滑块(
最小值:1,最大值:1,值:_值,
onChanged:(value)=>setState((){u value=value;})
));
}
}
类_SliderThumbImage扩展了SliderComponentShape{
静态常数thumbSideLength=60.0;
@凌驾
空白绘制(绘制上下文、偏移中心、,
{必需的动画激活动画,必需的动画启用动画,必需的bool isDiscrete,必需的TextPainter labelPainter,必需的RenderBox parentBox,必需的SliderThemeData sliderTheme,必需的TextDirection TextDirection,必需的double value,必需的double textScaleFactor,必需的sizeWithOverflow}){
最终画布=context.canvas;
偏移量thumbDrawOffset=偏移量(center.dx-(thumbSideLength/2),center.dy-(thumbSideLength/2));
canvas.drawRect(Rect.fromLTWH(thumbDrawOffset.dx,thumbDrawOffset.dy,thumbSideLength,thumbSideLength),
油漆()…颜色=颜色。黑色);
}
@凌驾
大小getPreferredSize(布尔值已启用,布尔值已分离){
返回大小(thumbSideLength、thumbSideLength);
}
}
类\u SliderTrack扩展SliderTrackShape{
静态常数宽度=184.0;
静态常数轨道高度=16.0;
@凌驾
Rect getPreferredRect(
{必需的RenderBox parentBox,Offset Offset=Offset.zero,必需的SliderThemeData sliderTheme,bool isEnabled=true,bool isDiscrete=true}){
从LTWH返回矩形(0,0,宽度,轨迹高度);
}
@凌驾
无效绘制(绘制上下文、偏移、,
{必需的RenderBox parentBox、必需的SliderThemeData sliderTheme、必需的动画启用动画、必需的偏移量thumbCenter、bool isEnabled=true、bool isDiscrete=true、必需的TextDirection TextDirection}){
最终画布=context.canvas;
帆布拉丝(
偏移量(Offset.dx,Offset.dy+轨道高度/2),
偏移量(Offset.dx+宽度,Offset.dy+轨高/2),
油漆()
…颜色=颜色。琥珀色
..strokeCap=strokeCap.round
..行程宽度=轨道高度);
}
}
这将呈现以下内容。黑色正方形的上半部分不可用于拖动,而滑块的整个下半部分可用于拖动。滑块的上半部分或滑块拇指应该是可选的


如果注释掉
trackShape
参数,则拇指的正确区域是可选择的。

可选择用于调整滑块值的滑块区域由轨迹尺寸和拇指尺寸配置。如果将拇指配置为50x50,轨迹配置为100x10,则可选择的区域是滑块的50x100边界框,而不是50x50拇指区域。例如,下面突出显示的区域是可选区域:

这也是没有主题的默认滑块的行为

考虑到这一点,我在这个边界框中适当地绘制了轨迹和拇指偏移

import 'package:flutter/material.dart';

const thumbSideLength = 60.0;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            SizedBox(height: 100),
            Row(children: [
              SizedBox(width: 100),
              CustomSlider()
            ])
          ]
        )
      )
    );
  }
}


class CustomSlider extends StatefulWidget {
  CustomSlider({Key? key}) : super(key: key);

  @override
  _CustomSliderState createState() => _CustomSliderState();
}

class _CustomSliderState extends State<CustomSlider> {
  double _value = .5;

  @override
  Widget build(BuildContext context) {
    return SliderTheme(
        data: SliderThemeData(
          thumbShape: _SliderThumbImage(),
          trackShape: _SliderTrack(), // Commenting results in correct selectable thumb region
        ),
        child: Slider(
          min: .1, max: 1, value: _value,
          onChanged: (value) => setState(() { _value = value; })
        ));
  }
}

class _SliderThumbImage extends SliderComponentShape {  
  @override
  void paint(PaintingContext context, Offset center,
      {required Animation<double> activationAnimation, required Animation<double> enableAnimation, required bool isDiscrete, required TextPainter labelPainter, required RenderBox parentBox, required SliderThemeData sliderTheme, required TextDirection textDirection, required double value, required double textScaleFactor, required Size sizeWithOverflow}) {
    final canvas = context.canvas;
    Offset thumbCenter = Offset(center.dx, thumbSideLength/2);

    canvas.drawRect(Rect.fromCenter(center: thumbCenter, width: thumbSideLength, height: thumbSideLength), Paint()..color = Colors.black);
  }

  @override
  Size getPreferredSize(bool isEnabled, bool isDiscrete) {
    return Size(thumbSideLength, thumbSideLength);
  }
}

class _SliderTrack extends SliderTrackShape {
  static const width = 184.0;
  static const trackHeight = 16.0;

  @override
  Rect getPreferredRect(
      {required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = true, bool isDiscrete = true}) {
    return Rect.fromLTWH(0, 0, width, trackHeight);
  }

  @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;

    canvas.drawLine(
      Offset(offset.dx, offset.dy + thumbSideLength/2),
      Offset(offset.dx + width, offset.dy + thumbSideLength / 2),
      Paint()
      ..color = Colors.amber
      ..strokeCap = StrokeCap.round
      ..strokeWidth = trackHeight);
  }
}
导入“包装:颤振/材料.省道”;
常数thumbSideLength=60.0;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
家:脚手架(
正文:专栏(
儿童:[
尺寸箱(高度:100),
世界其他地区(儿童:[
尺寸箱(宽度:100),
自定义滑块()
])
]
)
)
);
}
}
类CustomSlider扩展StatefulWidget{
CustomSlider({Key?Key}):超级(Key:Key);
@凌驾
_CustomSliderState createState()=>\u CustomSliderState();
}
类_CustomSliderState扩展状态{
双_值=.5;
@凌驾
小部件构建(构建上下文){
返回滑块主题(
数据:SliderThemeData(
拇指形状:_SliderThumbImage(),
trackShape:_SliderTrack(),//注释结果为正确的可选择拇指区域
),
子:滑块(
最小值:1,最大值:1,值:_值,
onChanged:(value)=>setState((){u value=value;})
));
}
}
类_SliderThumbImage扩展了SliderComponentShape{
@凌驾
空白绘制(绘制上下文、偏移中心、,
{必需的动画激活动画,必需的动画启用动画,必需的bool isDiscrete,必需的TextPainter labelPainter,必需的RenderBox parentBox,必需的SliderThemeData sliderTheme,必需的TextDirection TextDirection,必需的double value,必需的double textScaleFactor,必需的sizeWithOverflow}){
最终画布=context.canvas;
偏置拇指中心=偏置(center.dx,拇指边长/2);
canvas.drawRect(Rect.fromCenter(中心:拇指中心,宽度:拇指边长,高度:拇指边长),Paint()…color=Colors.black);
}
@凌驾
大小getPreferredSize(布尔值已启用,布尔值已分离){
返回大小(拇指)