You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Draggable组件回退动画实现求助:拖拽后滑回初始位置

实现Draggable拖拽后平滑滑回初始位置

下面是一个直接可用的代码示例,核心通过AnimatedPositioned结合状态管理,实现拖拽结束后的平滑回弹动画:

import 'package:flutter/material.dart';

class DraggableWithSnapBack extends StatefulWidget {
  const DraggableWithSnapBack({super.key});

  @override
  State<DraggableWithSnapBack> createState() => _DraggableWithSnapBackState();
}

class _DraggableWithSnapBackState extends State<DraggableWithSnapBack> {
  // 记录初始位置与当前实时位置
  Offset _initialPos = const Offset(100, 100);
  Offset _currentPos = const Offset(100, 100);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          AnimatedPositioned(
            left: _currentPos.dx,
            top: _currentPos.dy,
            // 自定义动画时长与曲线
            duration: const Duration(milliseconds: 350),
            curve: Curves.easeOutQuad,
            child: Draggable(
              data: Object(),
              // 拖拽时跟随的反馈组件
              feedback: Container(
                width: 100,
                height: 100,
                color: Colors.blue.withOpacity(0.7),
                child: const Center(child: Text('拖拽中')),
              ),
              // 拖拽时原位置的占位组件
              childWhenDragging: Container(
                width: 100,
                height: 100,
                color: Colors.grey.withOpacity(0.4),
              ),
              // 拖拽取消(未放入DragTarget)时触发回弹
              onDraggableCanceled: (velocity, offset) {
                setState(() => _currentPos = _initialPos);
              },
              // 监听拖拽过程,实时更新位置
              child: GestureDetector(
                onPanUpdate: (details) {
                  setState(() => _currentPos += details.delta);
                },
                child: Container(
                  width: 100,
                  height: 100,
                  color: Colors.blue,
                  child: const Center(child: Text('拖拽我')),
                ),
              ),
            ),
          ),
          // 测试用的拒绝型DragTarget
          Positioned(
            left: 200,
            top: 300,
            child: DragTarget(
              onWillAccept: (data) => false, // 拒绝接受拖拽数据
              builder: (context, candidateData, rejectedData) {
                return Container(
                  width: 150,
                  height: 150,
                  color: Colors.red,
                  child: const Center(child: Text('拒绝接受')),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

核心实现要点:

  • 用_currentPos控制组件实时位置,拖拽过程中通过GestureDetector的onPanUpdate更新坐标
  • 无论拖拽取消还是被DragTarget拒绝,只要将_currentPos重置为_initialPos,AnimatedPositioned就会自动触发平滑回弹动画
  • Draggable的feedback负责显示拖拽跟随组件,childWhenDragging可设置半透明占位提升视觉体验

替代方案与拓展思路:

  1. 完全自定义拖拽逻辑
    若不需要Draggable与DragTarget的交互,可以直接用GestureDetector处理onPanStart、onPanUpdate、onPanEnd,在onPanEnd时通过AnimationController驱动位置动画回归初始点,这种方式自由度更高,适合复杂拖拽场景。

  2. 利用Draggable的onDragEnd回调
    无需GestureDetector,在Draggable.onDragEnd中获取结束位置,再通过AnimatedPositioned动画回初始点,注意需手动计算位置偏移(Draggable本身位置不会随拖拽实时变化)。

  3. 第三方封装库
    比如draggable_widget这类库已经封装了拖拽回弹效果,适合快速搭建需求场景。

内容的提问来源于stack exchange,提问作者Dmitry_M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:12:33