Flutter如何实现按钮双向滑动(已完成正向滑动)
实现双向滑动按钮的完整方案
要实现双向滑动(左右均可滑至触发状态),你需要调整动画范围、拖拽逻辑和状态判断,以下是具体修改步骤和完整代码:
核心修改点
- 扩展动画控制器范围:把控制器取值范围从默认的
0.0-1.0改成-1.0-1.0,左滑对应负数值,右滑对应正数值 - 调整滑动动画的Tween:让动画从
Offset(-1.0, 0.0)到Offset(1.0, 0.0),对应按钮从最左滑到最右 - 修改拖拽处理逻辑:允许拖拽百分比范围扩展到
-1.0-1.0,并在拖拽结束时根据滑动距离判断是回弹还是滑到端点 - 增加反向滑动的状态处理:添加左滑完成后的图标显示逻辑,同时调整文本隐藏的触发条件
修改后的完整代码
class _SlideButtonState extends State<SlideButton> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<Offset> _slideAnimation; bool _showCheckIcon = false; // 右滑完成显示 bool _showCloseIcon = false; // 左滑完成显示 bool _hideSlideText = false; late Offset _dragStart; @override void initState() { super.initState(); // 扩展控制器范围到-1.0到1.0,支持双向滑动 _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 500), lowerBound: -1.0, upperBound: 1.0, ); // 动画覆盖从最左到最右的范围,对应控制器value从-1到1 _slideAnimation = Tween<Offset>( begin: const Offset(-1.0, 0.0), end: const Offset(1.0, 0.0), ).animate(_controller) ..addListener(() { setState(() {}); }) ..addStatusListener((status) { if (status == AnimationStatus.completed) { // 右滑至端点触发完成状态 setState(() { _showCheckIcon = true; _hideSlideText = true; }); } else if (status == AnimationStatus.dismissed) { // 左滑至端点触发初始状态(对应控制器lowerBound) setState(() { _showCloseIcon = true; _hideSlideText = true; }); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } void _onHorizontalDragUpdate(DragUpdateDetails details) { final dx = details.globalPosition.dx - _dragStart.dx; double slidePercent = dx / context.size!.width; // 限制滑动百分比在-1.0到1.0之间,避免超出按钮范围 slidePercent = slidePercent.clamp(-1.0, 1.0); _controller.value = slidePercent; } void _onHorizontalDragEnd(DragEndDetails details) { // 设置滑动阈值,超过按钮宽度一半则触发端点状态,否则回弹到中间 const threshold = 0.5; if (_controller.value > threshold) { // 滑到最右,执行正向动画 _controller.forward(); } else if (_controller.value < -threshold) { // 滑到最左,执行反向动画 _controller.reverse(); } else { // 回弹到初始中间位置 _controller.animateTo(0.0, duration: const Duration(milliseconds: 300)); } } @override Widget build(BuildContext context) { return GestureDetector( onHorizontalDragDown: (details) { _dragStart = details.globalPosition; // 拖拽开始时重置状态,允许再次滑动 if (_hideSlideText) { setState(() { _showCheckIcon = false; _showCloseIcon = false; _hideSlideText = false; }); } }, onHorizontalDragUpdate: _onHorizontalDragUpdate, onHorizontalDragEnd: _onHorizontalDragEnd, child: Container( height: 50.0, width: 250, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(25.0), ), child: Stack( alignment: Alignment.center, children: [ if (!_hideSlideText) ClipRRect( borderRadius: BorderRadius.circular(25.0), child: SlideTransition( position: _slideAnimation, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.arrow_back_ios, color: Colors.white), const SizedBox(width: 8), const Text( 'slide to continue or cancel', style: TextStyle( color: Colors.white, fontSize: 16.0, ), ), const SizedBox(width: 8), const Icon(Icons.arrow_forward_ios, color: Colors.white), ], ), ), ), // 右滑完成显示的对勾图标 AnimatedOpacity( opacity: _showCheckIcon ? 1.0 : 0.0, duration: const Duration(milliseconds: 250), child: const Icon( Icons.check, color: Colors.black, size: 30.0, ), ), // 左滑完成显示的关闭图标 AnimatedOpacity( opacity: _showCloseIcon ? 1.0 : 0.0, duration: const Duration(milliseconds: 250), child: const Icon( Icons.close, color: Colors.black, size: 30.0, ), ), ], ), ), ); } }
关键细节说明
- 动画控制器范围:通过
lowerBound: -1.0让控制器支持负数值,对应左滑的动画进度 - 拖拽结束逻辑:设置
0.5的阈值,只有滑动超过按钮宽度的一半时才会触发端点状态,否则回弹到中间,提升交互体验 - 状态重置:在
onHorizontalDragDown时重置图标显示状态,允许用户再次滑动 - UI调整:添加了左右箭头提示双向滑动,同时增加左滑完成的关闭图标,匹配你期望的效果
内容的提问来源于stack exchange,提问作者Mansoor Malik
相关产品推荐
相关产品推荐

