Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

用户故事地图

既然我们已经明确了“领域模型”与“UI组件”分离的设计哲学,那么 StoryMapCanvas 的设计重点就不再是“存数据”,而是“展示”与“交互”。

在 Flutter 中,StoryMapCanvas 应该是一个巨大的、可滚动的容器组件。它的核心职责是协调 ActivityLane(泳道)的布局,并处理复杂的拖拽排序逻辑。

以下是 StoryMapCanvas 的详细设计方案:

🧱 1. 核心布局结构

StoryMapCanvas 本质上是一个 “横向滚动的列表,里面装着纵向堆叠的卡片”。

代码结构示意: Widget build(BuildContext context) { return SingleChildScrollView( scrollDirection: Axis.horizontal, // 横向滚动 child: Row( children: widget.mapData.activities.map((activity) { return ActivityLane( // 每一个泳道 activity: activity, onTaskReorder: _handleTaskReorder, // 传递拖拽排序的回调 onStoryReorder: _handleStoryReorder, ); }).toList(), ), ); }

🎨 2. 视觉元素构成

为了让这张“画布”不仅仅是白板,你需要绘制一些辅助视觉元素。建议使用 Stack 叠加在滚动视图之上:

🖱️ 3. 核心交互逻辑

StoryMapCanvas 是交互的中枢,它需要处理复杂的拖拽事件。

A. 拖拽排序 (Drag and Drop) 这是画布的灵魂。用户应该能:

技术实现建议:

B. 缩放与视图控制

📦 4. 数据流设计

StoryMapCanvas 本身不应该持有数据状态(除非你用 StatefulWidget 搞原型),它应该是一个“纯展示组件”。

🛠️ 5. 性能优化策略

由于故事地图可能包含成百上千张卡片,性能是关键。

📌 总结:StoryMapCanvas 的职责清单

为了让你在写代码时不跑偏,StoryMapCanvas 应该只做以下几件事:

  1. 画布:提供一个宽大的、可横向滚动的平面。

  2. 布局:把 ActivityLane 水平排列,并绘制背景网格。

  3. 交互:处理卡片的拖拽、移动、排序。

  4. 分界:展示并允许调整 MVP 虚线。

  5. 通讯:把用户的操作转化为事件,告诉外面的“世界”:“数据变了,请更新”。

不要在 StoryMapCanvas 里做:

这样设计,你的 StoryMapCanvas 就会是一个高性能、高内聚、低耦合的“画布”。