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