fx-event-emitter
FxEvent 全局事件总线使用规范。在需要跨模块通信(举报、拉黑、导航、toast 等)且不想通过回调层层传递时激活,确保使用正确的事件定义、发送和监听模式。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
FxEvent 全局事件总线使用规范。在需要跨模块通信(举报、拉黑、导航、toast 等)且不想通过回调层层传递时激活,确保使用正确的事件定义、发送和监听模式。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
GitHub Release 发布操作规范。在需要发布版本、上传安装包到 GitHub Release 时激活,确保使用正确的命令和流程。
确认/删除等交互弹框统一使用 showTolyPopPicker 底部弹出样式。在需要弹出确认框、删除确认、操作选择时激活,确保交互风格一致。
后端服务启动与数据库操作规范。在需要启动后端服务、运行 API 测试、执行数据库迁移或遇到连接错误时激活,确保使用正确的命令和流程。
功能归档规范。在归档功能版本、更新功能网、创建存档快照时激活,确保节点编号正确、网络图完整。
使用 tolyui_mediax 实现媒体预览。适用于图片九宫格展示、全屏预览、手势缩放、视频播放、Hero 动画等场景。
Flutter Widget/Page 组件代码评审技能。在需要审查组件代码质量、发现设计问题时激活,确保输出结构化的问题清单和改进建议。
| name | fx-event-emitter |
| description | FxEvent 全局事件总线使用规范。在需要跨模块通信(举报、拉黑、导航、toast 等)且不想通过回调层层传递时激活,确保使用正确的事件定义、发送和监听模式。 |
| metadata | {"model":"manual","last_modified":"Wed, 11 Jun 2026 00:00:00 GMT"} |
flash_im_friend 触发 flash_im_chat 的举报 Sheet)位置:client/modules/flash_shared/lib/src/fx_emitter.dart
class FxEmitter {
factory FxEmitter() => _instance; // 单例
Stream<FxEvent> get stream; // 全部事件流
StreamSubscription<E> on<E extends FxEvent>(void Function(E) handler); // 按类型监听
void emit(FxEvent event); // 发送事件
}
class FxEvent {
const FxEvent();
void emit() => FxEmitter().emit(this); // 便捷发送
}
在 flash_shared/lib/src/fx_emitter.dart 底部或独立文件中定义:
/// 举报用户事件
class ReportUserEvent extends FxEvent {
final String userId;
final String nickname;
const ReportUserEvent({required this.userId, required this.nickname});
}
/// 拉黑用户事件
class BlockUserEvent extends FxEvent {
final String userId;
final String nickname;
const BlockUserEvent({required this.userId, required this.nickname});
}
事件定义放在 flash_shared 中(底层包),保证所有模块都能访问。
// 一行搞定,不需要 context、不需要回调、不需要依赖上层模块
ReportUserEvent(userId: '123', nickname: '张三').emit();
在 home_page.dart 或 App shell 的 initState 中:
final List<StreamSubscription<dynamic>> _eventSubs = [];
void _setupEventListeners() {
_eventSubs.add(FxEmitter().on<ReportUserEvent>((ReportUserEvent e) {
// 弹举报 Sheet、调 API、toast...
}));
_eventSubs.add(FxEmitter().on<BlockUserEvent>((BlockUserEvent e) async {
await dio.post('/api/blocks', data: {'blocked_id': int.parse(e.userId)});
// toast
}));
}
@override
void dispose() {
for (final sub in _eventSubs) { sub.cancel(); }
super.dispose();
}
参考 fx_trace 的 FxEmitterMixin:
mixin FxEmitterMixin<T extends StatefulWidget> on State<T> {
StreamSubscription<FxEvent>? _subscription;
@override
void initState() {
super.initState();
_subscription = FxEmitter().stream.listen(onEvent);
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
void onEvent(FxEvent event); // 子类实现
}
当发送方需要等待处理结果时:
class PickFileEvent extends AsyncFxEvent<String?> {}
// 发送并等待
final String? path = await PickFileEvent().emitAsync(timeout: Duration(seconds: 30));
// 处理方
FxEmitter().on<PickFileEvent>((e) {
final result = await showFilePicker();
e.complete(result);
});
| 原则 | 说明 |
|---|---|
| 事件只携带数据 | 事件类只有 final 字段,不带逻辑 |
| 发送方不关心谁处理 | emit 后就完事,解耦 |
| 监听方集中在顶层 | 避免散落各处,装配逻辑清晰 |
| 订阅必须取消 | dispose 中 cancel,成对出现 |
| 事件定义在底层包 | 放 flash_shared,所有模块可见 |
| 机制 | 适用 | 方向 |
|---|---|---|
| FxEvent | 跨模块、跨页面 | 任意 → 任意 |
| Cubit emit | 同模块内状态管理 | Logic → View |
| 回调参数 | 父子 Widget | Child → Parent |
| WS Stream | 服务端推送 | Server → Client |
| 事件 | 发送方 | 处理方 | 用途 |
|---|---|---|---|
ReportUserEvent | user_profile_page | home_page | 弹举报 Sheet |
BlockUserEvent | user_profile_page | home_page | 调拉黑 API + toast |