1. 前语
最近要完成一个小需求,涵盖了许多知识点,比方手势、动画、布局等。挺有意思的,写出来和我们分享一下。如下所示,分为上下两层;当左右滑时,上层会随偏移量而平移,从而让上层发生滑动手势显隐的作用:
标题 | |
---|---|
这儿上层经过不透明度 0.2 的蓝色示意,实际运用时能够改为透明色。许多直播间的浮层就是这种交互逻辑,经过右滑来隐藏浮层。
直播 | 右滑中 |
---|---|
2. 完成思路
思路其实十分简略,监听横向滑动的手势事情,依据偏移量让上层组件进行偏移。当放手时,依据偏移量是否到达宽度的一半,运用动画进行移出或者封闭。
偏移的完成方式有许多,但需要自由地进行布局和矩阵改换、透明度,并且需要支持动画的改变,Flow 组件是一个十分不错的选择。 Flow 组件能够经过署理类对子组件进行自定义布局,灵活性极强;如果是 CustomPaint
是 制作之王 能够制作万物,那么 Flow
就是 布局之王,能够摆放万物。三年前写过一篇介绍 Flow 运用的文章: 《【Flutter高档玩法- Flow 】我的方位我做主》 。 本文就不对 Flow 的基础运用进行介绍了。
别的,在滑动过程中需要注意约束偏移量,使偏移量在 0~size.width
之内;当放手时,经过动画控制器来驱动动画,运用补间让偏移量运动到 0
(翻开) 或 size.width
(封闭) 。当封闭时,在右下角展现一个按钮用于点击展开:
3. 布局的代码完成
Flow 组件布局最重要的是完成 FlowDelegate
,在其间的 paintChildren
办法中完成布局的逻辑。和 CustomPainter
相似,FlowDelegate
的完成类也能够经过 super
结构为 repaint
入参设置可监听对象。可监听对象的改变会触发 paintChildren
重新制作:
SwipeFlowDelegate
完成类再结构时传入可监听对象 offsetX
,在制作索引为 1
的孩子时,经过 Matrix4
进行偏移。这样只要在手势水平滑动中,更新 offsetX 值即可。别的,能够依据 offsetX.value
是否到达 size.width
知道是否是封闭状态,如果现已封闭,制作按钮。
class SwipeFlowDelegate extends FlowDelegate {
final ValueListenable<double> offsetX;
SwipeFlowDelegate(this.offsetX) : super(repaint: offsetX);
@override
void paintChildren(FlowPaintingContext context) {
Size size = context.size;
context.paintChild(0);
Matrix4 offsetM4 = Matrix4.translationValues(offsetX.value, 0, 0);
context.paintChild(1, transform: offsetM4);
// 偏移量关于父级尺寸
if (offsetX.value == size.width) {
Matrix4 m1 = Matrix4.translationValues(size.width / 2 - 30, size.height / 2 - 30, 0);
context.paintChild(2, transform: m1);
Matrix4 m2 = Matrix4.translationValues(size.width / 2 - 30, -(size.height / 2 - 50), 0);
context.paintChild(3, transform: m2);
}
}
@override
bool shouldRepaint(covariant SwipeFlowDelegate oldDelegate) {
return oldDelegate.offsetX.value != offsetX.value;
}
}
从这儿能够看出,FlowDelegate
的最大优势是能够自定义孩子的制作与否,还能够在制作时经过 Matrix4
对孩子进行矩阵改换,还有可选参数能够控制透明度。接下来运用 Flow
组件时,供给 SwipeFlowDelegate
,并在 children 列表中顺次放入子组件。其间前两个组件由外界传入,分别是底组件和上层组件,这样组件的布局就完成了,接下来监听事情,更新 factor
即可:
final ValueNotifier<double> factor = ValueNotifier(0);
Flow(
delegate: SwipeFlowDelegate(factor),
children: [
widget.content,
widget.overflow,
GestureDetector(
onTap: open,
child: const Icon(Icons.menu_open_outlined, color: Colors.white)),
GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: const Icon(Icons.close, color: Colors.white))
],
)
4. 手势的监听
这儿手势的处理是十分简略的,经过 GestureDetector
监听水平拖拽事情。在 onHorizontalDragUpdate
中依据拖拽的偏移量更新 factor
的值,其间经过 .clamp(0, widget.width)
能够约束偏移量的取值区间。
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragUpdate: _onHorizontalDragUpdate,
onHorizontalDragEnd: _onHorizontalDragEnd,
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: widget.width,
child: Flow( delegate:// 同上,略...
);
}
void _onHorizontalDragUpdate(DragUpdateDetails details) {
double cur = factor.value + details.delta.dx;
factor.value = cur.clamp(0, widget.width);
}
void _onHorizontalDragEnd(DragEndDetails details) {
if (factor.value > widget.width / 2) {
close();
} else {
open();
}
}
最后在 _onHorizontalDragEnd
回调中,依据当前偏移量是否大于一般宽度,决议封闭仍是翻开。期间过程运用动画进行偏移量的过渡改变。
5. 动画的运用
动画的运用,主要是经过 AnimationController
动画控制器来驱动数值的改变;在放手时 Tween
创立补间动画器,监听动画器数值的改变更新偏移量。这样偏移量就能够在指定时间内,在两个值之间渐变,从而发生动画作用。比方抬手时,open
办法是让偏移量从当前方位改变到 0
:
class _ScrollHideWrapperState extends State<ScrollHideWrapper> with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
final ValueNotifier<double> factor = ValueNotifier(0);
@override
void initState() {
super.initState();
_ctrl = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
}
@override
Widget build(BuildContext context) {
// 略同...
}
// 动画封闭
Future<void> close() async {
Animation<double> anim = Tween<double>(begin: factor.value, end: widget.width).animate(_ctrl);
anim.addListener(() => factor.value = anim.value);
await _ctrl.forward(from: 0);
}
// 动画翻开
Future<void> open() async {
Animation<double> anim = Tween<double>(begin: factor.value, end: 0).animate(_ctrl);
anim.addListener(() => factor.value = anim.value);
await _ctrl.forward(from: 0);
}
}
如果想让动画的改变非匀速,能够运用 Curve 来控制动画曲线。这样,基于 Flow 完成的自定义布局,就能够依据手势和动画,完成特定的交互功能。从这儿能够看出 Flow 自定义布局的灵活性十分强,许多疑难杂症,都能够运用它来完成。
比方企业微信中:侧滑展现左栏,并且上层不会全部消失,经过 Flow 来自定义布局就很容易完成。我们能够基于本文,自己完成一下作为操练。那本文就到这儿,谢谢观看 ~
标题 | 封闭 |
---|---|