总体思路
侧滑控件的实现原理: 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<double> animation,
Animation<double> secondaryAnimation) {
...
return slideWindowWidget;//page是全屏的
}
}
布局
这个widget是一个Stack结构,底层是全屏的Container蒙层,上层是一个带侧滑动画的内容widget,暂时只能是SizedBox,因为侧滑动画需要知道widget的宽度,而SizedBox是固定宽度的。
Stack(children: <Widget>[
//蒙层
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(),还是回到第一种情况