Files
social-app/docs/superpowers/specs/2026-03-20-todo-quadrant-drag-design.md
T

114 lines
3.1 KiB
Markdown
Raw Normal View History

# 待办事项四象限拖拽交互设计
## 概述
四象限待办页面支持待办项在象限内排序以及跨象限拖拽移动,同时保持与后端的数据同步。
## 交互设计
### 拖拽状态
| 状态 | 视觉反馈 |
|------|----------|
| 按住(未拖拽) | 卡片 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<TodoResponse> _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