flutter手写一个非侵入式Drawer

总体思路

侧滑控件的实现原理: Flutter中Navigator是用来控制路由栈的,使用方式如下:

Navigator.of(context).push(route);

push接收一个Route,这个Route负责给出具体的widget,普通的Route在显示自己的page时会覆盖掉原本的页面,而PopupRoute就不会,他的效果类似于android中的popupWindow,自带蒙层。

继承PopupRoute,修改参数,重写buildPage,在buildPage中返回一个带侧滑动画的widget

class SlideWindow extends PopupRoute {
  ...
  @override
  Color get barrierColor => null;//去掉蒙层

  @override
  Widget buildPage(BuildContext context, Animation animation,
      Animation secondaryAnimation) {
  ...
    return slideWindowWidget;//page是全屏的
  }

}

布局

这个widget是一个Stack结构,底层是全屏的Container蒙层,上层是一个带侧滑动画的内容widget,暂时只能是SizedBox,因为侧滑动画需要知道widget的宽度,而SizedBox是固定宽度的。

Stack(children: [
        //蒙层
      GestureDetector(
        onTap: widget.onTapOutsize,
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          color: colorAnimation.value,
        ),
      ),
        //内容层
      Container(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        alignment: Alignment.centerRight,
        //靠右竖直居中的内容widget
        child: Transform.translate(
          offset: Offset(widgetAnimation.value, 0),
          child: widget.child,
        ),
      )
    ]);

动画

在Flutter中,补间动画Tween的原理和Android中类似,规定value变化范围,value每次变化时setState(),根据最新的value去重建widget,在value变化的过程中,widget时不断重建的,某些属性也在不断被修改,这样就达到了动画效果。

这里需要用到两种动画,一个是内容层的平移,一个是蒙层的颜色变化,代码如下,在初始化状态时设置动画

@override
void initState() {
  super.initState();
    //设置动画控制器
  controller = new AnimationController(
    duration: widget.duration,
    vsync: this,
  );
  //设置插值器
  Animation curve = CurvedAnimation(
      parent: controller, curve: Curves.fastLinearToSlowEaseIn);
  //设置内容层平移动画
  widgetAnimation =
      new Tween(begin: widget.child.width, end: 0.0).animate(curve)
        ..addListener(() {
          setState(() {});
        });
  //设置蒙层颜色动画
  colorAnimation = new ColorTween(
          begin: Colors.black.withOpacity(0),
          end: Colors.black.withOpacity(0.4))
      .animate(curve);
  //开始动画
  controller.forward();
}

然后内容需要用一个Transform变换控件包裹,这个控件可以对child做一些变换,例如移动位置

Transform.translate(
  offset: Offset(widgetAnimation.value, 0),
  child: widget.child,
)

响应退出

调用Navigator.pop()

因为这是一个Route,所以是受Navigator控制的,代码中调用了Navigator.pop()时我们的页面是会消失的,这个消失不做处理的话是不带侧滑动画,也就是很难看,而在pop的过程中,系统会调用Route的didpop方法,所以可以重写这个方法在里面反转动画

@override
bool didPop(result) {
  slideWindowWidget.state.controller.reverse();//反转
  return super.didPop(result);
}
触摸了蒙层

这种情况需要自己去控制,在蒙层上设置GestureDetector,在响应中调用Navigator.pop(),又回到第一种情况

按到了物理返回键

这种情况貌似在ios上不会出现,但是android设备是有返回键的,按了返回键后,系统同样会调用Navigator.pop(),还是回到第一种情况

你可能感兴趣的:(flutter手写一个非侵入式Drawer)