Flutter高仿驾考宝典之顺序练习——底部下拉框

792 阅读1分钟

首先效果上图:

再上代码

now,let‘s go。

分为三节:

1、底部下拉框,用于概括全局

2、中间页面的滑动切换

3、两个地方的配合,增加阴影,点击阴影下拉框回退等

本节为第一节底部下拉框

思想:在Stack中包含,并且位于底部,并将框框控件用Transform.translate包住,通过offset使其向底部偏移。给框框顶部设置手势,手势通过改变offset而改变框框位置。再给框框设置点击事件,以及通过距离和速度判断是应该上去还是下去,这点通过动画实现。

PageChangeMiddle是身子,DragContainer是底部,我们只用关心底部即可 此处通过Align 放在Stack的底部,

Stack(
      children: <Widget>[
        PageChangeMiddle(
          controll: sliverSelfControll,
        ),
        Align(
                  alignment: Alignment.bottomCenter,
                  child: DragContainer()
            ),
        ]
    )

此处通过translate使其向y轴偏移,偏移量为offsetDistance

//practiceBottomDrawerHeight是这个框框的最大高度
double practiceBottomDrawerHeight = 500;

//videoFixHeight是框框顶部高度
double videoFixHeight = 50;

//offsetDistance即初始时需要偏移的高度
double offsetDistance = practiceBottomDrawerHeight - videoFixHeight;

Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(0.0, offsetDistance),
      child: RawGestureDetector(
        gestures: {BottomDrawerVerticalDragGestureRecognizer: getRecognizer()},
        child: _DrawerWidget(),
      ),
    );
  }

RawGestureDetector即给设置手势,此处仅仅粘贴核心代码,若要仔细研究,请去github


  ///垂直移动
  void _onUpdate(DragUpdateDetails details) {
    print('垂直移动${details.delta}');
    offsetDistance = offsetDistance + details.delta.dy;
    //设置界限
    if (offsetDistance <= 0) {
      //当offsetDistance<=0时,即达到最高点禁止滑动
      offsetDistance = 0;
    } else if (offsetDistance >=
        (practiceBottomDrawerHeight - videoFixHeight)) {
      //当offsetDistance<=0时,即达到最高点禁止滑动
      offsetDistance = practiceBottomDrawerHeight - videoFixHeight;
    } else {
      //offset在允许的范围改变,即需要刷新
      setState(() {});
    }
  }

以上即根据手势滑动。 当手指离开时,需要确定是展开还是回缩。
当然,前面还需要初始化动画

  void _onEnd(DragEndDetails details) {
    print('离开屏幕');
    double halfOffset = (practiceBottomDrawerHeight - videoFixHeight) / 2;
    double velocityY = details.velocity.pixelsPerSecond.dy;
    //先根据速度判断
    if (velocityY.abs() > 400) {
      if (velocityY > 0) {
        endSliver(false);
      } else {
        endSliver(true);
      }
    } else {
    //如果速度太低则根据滑动距离判断
      if (offsetDistance >= halfOffset) {
        endSliver(false);
      } else {
        endSliver(true);
      }
    }
  }
  
   void endSliver(isShow) {
    if (isShow) {
      print('展开');
      start = offsetDistance;
      end = 0;
    } else {
      print('回收');
      start = offsetDistance;
      end = practiceBottomDrawerHeight - videoFixHeight;
    }
    animalController.value = 0.0;
    animation = Tween(begin: start, end: end).animate(curve)
      ..addListener(() {
        offsetDistance = animation.value;
        widget.onOffSliver(offsetDistance);
        setState(() {});
      });
    animalController.forward();
  }

封装这个endSliver是非常到位的。。。当我们点击底部展开,只需要加个点击事件然后调用 endSliver(true);即可,回收也是同理。

  GestureDetector(
            child: _buttonItemCount(
                'images/practice_count.png', '${current + 1}', '$countNum'),
            onTap: () {
              endSliver(true);
            },
          )

下拉框就是这么简单。如果觉得能看,请看下节。