Flutter学习笔记 —— 自定义Slider滑动条组件

前言

最近在做一个需要自定义Slider组件的功能,因为原生的那个不能配置相关的Circle 以及宽高,既然不能,那我们自己重写一个

思路

基础功能

在开始实现之前,我们先来想一想一个滑动条拥有那些基础功能

当然还可以添加其他属性,如果你是需要做缩放功能的可以加一个scale

缩放值 与 点位同步

这个点是非常重要的,也是重点功能!

本文自定义的是一个控制缩放的滑动条,以下参数值皆为示例参考,可根据实际场景自行修改

1.scale最大值为4.0,scale最小值为1.0

2.pointX最大偏移量为(外层父级最大宽度 – 15)

点位与缩放同步的规律则为

Point - > 0-9
Scale -> 1.0 -1.9
Point -> 10 -20
Scale -> 2.0 - 3.0
Point -> 20 - 30
Scale -> 3.0 - > 4.0

Point代表在水平触摸的实际点位dx偏移量,Scale代表实际缩放量

通过这个规律我们可以得出

在点位信息小于10的情况下,我们则以最小缩放值1.0取值范围内递增0.1

在点位信息超出10的情况下,我们则以每次(点位信息/10)方式并不能直接满足!

需要以小数点前的具体点位 /10之后,再+1.0

注:
为什么要采用小于10的时候取值范围每次递增0.1呢?
因为当PointX为10时,根据Point/10 = 1.0则缩放值从头1.0开始,而当点位为10时或者大于10的情况下,我们要在每次小数点前的具体点位值+1.0,则PointX = 10 Scale = 2.0 ,避免了重头开始情况!

实现手段

创建一个StatefulWidget

新增 ScaleSlider 因为缩放需要实时更新状态,所以我们选择 StatefulWidget

ScaleSlider.dart
class ScaleSlider extends StatefulWidget {
  //这里因为我使用的是GetX框架,这里携带了logic & state,实际应用中可以不需要携带,缩放值在state中
  final ReadLogic readLogic;
  final ReadState readState;
  final double max;
  final double min;
  final ValueChanged<double> onChanged;
  final bool flag;

  const ScaleSlider(this.readLogic, this.readState, this.max, this.min,
      this.flag, this.onChanged,
      {Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return ScaleSliderState();
  }
}

State

ScaleSliderState

class ScaleSliderState extends State<ScaleSlider>{
  String assetsPrefix = "assets/new_blue/txt/index/scale/";
  double _value = 0.0;
  GlobalKey globalKey = GlobalKey();
  GlobalKey globalKey2 = GlobalKey();
  double maxOffsetX = 0;
  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Log.i("页面加载完毕!");
      //根据全局Key获取最大偏移量
      final RenderBox renderBox =
      globalKey.currentContext!.findRenderObject() as RenderBox;
      Size targetSize = renderBox.size;
      maxOffsetX = targetSize.width - 15;
      print('最大偏移量: ${maxOffsetX}');
    });
    // TODO: implement initState
    super.initState();
  }


  @override
  Widget build(BuildContext context) {
    double scaleResult = (widget.readState.scale * 10);
    return GestureDetector(
        onHorizontalDragUpdate: (details) {
          double pointsX = details.localPosition.dx;
          print('当前位置:${details.localPosition.dx}');
          double minPoints = 0;
          double scaleValue = 0;
          scaleValue = (pointsX / 10);
          if(details.localPosition.dx >=maxOffsetX){
            _value = maxOffsetX;
            pointsX = maxOffsetX;
            scaleValue = widget.max;
            print('到最大!');
          }
          else if(details.localPosition.dx <=minPoints){
            _value = 0;
            pointsX = widget.min;
            scaleValue = widget.min;
            print('到最小!');
          }else{
            String pointXString = pointsX.toString();
            //如果点位小于10的全部视为缩放率1.N小数
            if(pointsX < 10){
              scaleValue = (widget.min) + scaleValue;
            }else{
              //小数点前具体数额
              String subStartNumber = pointXString.substring(0,pointXString.indexOf("."));
              //转比例
              double targetNumber = double.parse(subStartNumber)/ 10;
              double finishValue = (targetNumber + 1.0);
              scaleValue = finishValue;
            }
          }
          setState(() {
            _value = pointsX;
            widget.readState.validScaleControl.value = false;
          });
          Log.i("当前点位信息: ${pointsX},缩放率:${scaleValue}");
          widget.onChanged(scaleValue);
        },
        child: Container(
          key: globalKey,
          child: Stack(
            children: [
              //  底部条
              Positioned(
                left: 0.rpx,
                top: 18.rpx,
                child: SizedBox(
                  height: 5.rpx,
                  // width: Get.width,
                  child: Image.asset(
                    "${assetsPrefix}slider.png",
                    fit: BoxFit.fill,
                  ),
                ),
              ),
              // 滚动条
              Positioned(
                left: widget.flag
                    ? scaleResult <= (widget.min * 10)
                        ? 0
                        : scaleResult >= (widget.max * 10)
                            ? maxOffsetX
                            : scaleResult
                    : _value,
                top: 5.rpx,
                child: SizedBox(
                  width: 30.rpx,
                  height: 30.rpx,
                  child: Image.asset("${assetsPrefix}circle.png"),
                ),
              ),
            ],
          ),
        ));
  }
}

结束语

这个自定义功能具体说难不难,主要是根据实际应用场景去更改组件具体实现算法或者逻辑,如果你有更好的想法欢迎评论!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇