# 待办事项四象限拖拽交互设计 ## 概述 四象限待办页面支持待办项在象限内排序以及跨象限拖拽移动,同时保持与后端的数据同步。 ## 交互设计 ### 拖拽状态 | 状态 | 视觉反馈 | |------|----------| | 按住(未拖拽) | 卡片 scale 1.0,轻微阴影 | | 拖拽开始 | 卡片 scale 1.03 + 阴影加深,原位置保留半透明占位框 | | 拖拽中 | 卡片跟随手指(transform),目标象限边框高亮发光 | | 释放-象限内排序 | 卡片平滑移动到新位置(200ms ease-out) | | 释放-跨象限移动 | 卡片以 spring 动画弹入目标位置 | | 操作完成 | 显示成功 Toast | ### 动画参数 - **micro-interaction**: 150-300ms - **easing**: ease-out 进入,ease-in 退出 - **spring**: 用于跨象限移动,natural feel - **scale feedback**: 0.95-1.05 on press - **exit faster than enter**: 退出时长是进入的 60-70% ### 防误触 - 拖拽启动延迟:100-150ms 确认是长按而非点击 - 仅在按住并移动超过阈值后启动拖拽 ## 数据流 ### 状态管理 ``` _QuadrantScreenState ├── List _todos ├── DragState _dragState (null / dragging) └── int? _dragTargetQuadrant (1, 2, 3) ``` ### API 交互 1. **象限内排序**:调用 `PUT /todos/{id}` 更新 `priority` 和 `sort_order` 2. **跨象限移动**:调用 `PUT /todos/{id}` 更新 `priority` ### 乐观更新 - 用户释放后立即更新本地 UI - 后端请求失败时回滚 + 显示错误 Toast ## 组件结构 ``` TodoQuadrantsScreen ├── _QuadrantDragContainer (LongPressDraggable + DragTarget) │ ├── _QuadrantCard (象限容器) │ └── _TodoDragItem (可拖拽待办项) └── _DragFeedbackWidget (拖拽中的视觉反馈) ``` ## 状态定义 | 状态 | 描述 | |------|------| | `idle` | 正常显示 | | `dragging` | 正在拖拽某项 | | `dragOverQuadrant` | 拖拽到某象限上方 | | `reordering` | 正在执行排序动画 | ## 优先级定义 | 象限 | Priority Value | |------|---------------| | 重要紧急 | 1 | | 紧急不重要 | 3 | | 重要不紧急 | 2 | ## 视觉规范 ### 卡片样式 - **正常**: `color: AppColors.todoCardBg`, `borderRadius: 14px` - **拖拽中**: `opacity: 0.5` 在原位置显示占位 - **跟随手指**: `scale: 1.03`, `shadow: elevated` ### 象限边框高亮 - **正常**: `border: 1px solid {quadrantBorderColor}` - **dragOver**: `border: 2px solid AppColors.blue400`, `boxShadow: 0 0 12px AppColors.blue200` ### 插入指示器 - 高度 2px,圆角 1px - 颜色:`AppColors.blue500` - 位置:两个待办项之间 ## 错误处理 | 场景 | 处理方式 | |------|----------| | 后端请求失败 | 回滚本地状态,显示错误 Toast | | 网络断开 | 显示网络错误提示 | | 并发冲突 | 以最新数据为准,提示用户刷新 | ## 实现要点 1. 使用 Flutter `LongPressDraggable` + `DragTarget` 实现拖拽 2. 使用 `AnimatedContainer` / `AnimatedPositioned` 实现平滑动画 3. 乐观更新:先更新 UI,后请求后端 4. 拖拽反馈使用 `Transform` 而非改变位置,避免 CLS