fx-event-emitter
FxEvent 全局事件总线使用规范。在需要跨模块通信(举报、拉黑、导航、toast 等)且不想通过回调层层传递时激活,确保使用正确的事件定义、发送和监听模式。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
FxEvent 全局事件总线使用规范。在需要跨模块通信(举报、拉黑、导航、toast 等)且不想通过回调层层传递时激活,确保使用正确的事件定义、发送和监听模式。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional 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 |