smithery/hunmer

event-listener-container

为Flutter插件视图添加事件监听容器,自动管理事件订?

Installation

$ npx skills add smithery/hunmer --skill event-listener-container

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/hunmer.

npx skills add smithery/hunmer

Browse all from smithery/hunmer

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,759 B
  • docs SUMMARY.md 336 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

EventListenerContainer

为Flutter插件视图添加事件监听容器,自动管理事件订阅生命周期。

Workflow

1. Analyze Target File

识别以下内容:

  • State class 或 StatelessWidget
  • 需要监听的事件名称列表(如 taskadded, taskupdated)
  • UI 更新逻辑(setState() 或控制器方法调用)

2. Add Import

import 'package:Memento/widgets/event_listener_container.dart';

3. Wrap UI with EventListenerContainer

在需要响应事件的 UI 外层包裹组件:

EventListenerContainer(
  events: ['task_added', 'task_updated', 'task_deleted'],
  onEvent: () => setState(() {}),
  child: MyContentWidget(),
)

4. Combine with AnimatedBuilder (Optional)

当同时依赖控制器状态和事件时:

EventListenerContainer(
  events: ['task_added', 'task_updated', 'task_deleted'],
  onEvent: () {},  // 事件触发但不直接更新
  child: AnimatedBuilder(
    animation: myController,
    builder: (context, _) {
      return MyContentWidget(data: myController.data);
    },
  ),
)

5. Multiple Independent Listeners

同一页面可以有多个独立的监听器:

Column(
  children: [
    // 监听数据变更事件
    EventListenerContainer(
      events: ['data_changed'],
      onEvent: () => setState(() => _dataVersion++),
      child: DataDisplayWidget(),
    ),
    // 监听UI相关事件
    EventListenerContainer(
      events: ['theme_changed', 'locale_changed'],
      onEvent: () => _refreshTheme(),
      child: ThemedWidget(),
    ),
  ],
)

6. Conditional Update (Optional)

在 onEvent 中添加条件判断:

EventListenerContainer(
  events: ['item_added', 'item_updated', 'item_deleted'],
  onEvent: () {
    final eventName = EventManager.instance.getLatestEventName() ?? '';
    if (eventName == 'item_deleted' && shouldSkipDeleteUpdate) {
      return;
    }
    setState(() {});
  },
  child: MyContentWidget(),
)

Detection Patterns

Event Names

查找以下模式的事件名称:

事件类型 命名示例 说明
添加 taskadded, notecreated 使用过去式
更新 taskupdated, settingschanged 使用过去式
删除 taskdeleted, itemremoved 使用过去式
完成 taskcompleted, goalachieved 使用过去式
状态变更 statuschanged, visibilityupdated 使用过去式

Controller Integration

查找以下模式:

  • 控制器继承自 ChangeNotifier
  • 使用 AnimatedBuilder 响应控制器变化
  • 控制器有 refreshFromRemote() 或类似方法

Comparison: Manual vs EventListenerContainer

Manual Approach (Before)

class MyView extends StatefulWidget {
  const MyView({super.key});

  @override
  State<MyView> createState() => _MyViewState();
}

class _MyViewState extends State<MyView> {
  final List<(String, void Function(EventArgs))> _subscriptions = [];

  @override
  void initState() {
    super.initState();
    _registerListeners();
  }

  void _registerListeners() {
    void handler1(EventArgs args) {
      if (mounted) setState(() {});
    }
    EventManager.instance.subscribe('task_added', handler1);
    _subscriptions.add(('task_added', handler1));
    // ... 更多事件
  }

  @override
  void dispose() {
    for (final (eventName, handler) in _subscriptions) {
      EventManager.instance.unsubscribe(eventName, handler);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(children: [...]);
  }
}

EventListenerContainer (After)

class MyView extends StatelessWidget {
  const MyView({super.key});

  @override
  Widget build(BuildContext context) {
    return EventListenerContainer(
      events: ['task_added', 'task_updated', 'task_deleted'],
      onEvent: () => setState(() {}),
      child: Column(children: [...]),
    );
  }
}

代码量对比:

  • 手动方式:约 50 行
  • EventListenerContainer:约 8 行
  • 减少约 84% 的模板代码

Best Practices

1. Minimize Scope

只在需要响应事件的最小 UI 区域使用,避免不必要的重建。

2. Group Related Events

将相关事件分组,不要一次性监听所有事件。

3. Combine with Controller

对于复杂逻辑,将更新逻辑封装在控制器中,onEvent 只调用控制器方法:

EventListenerContainer(
  events: ['external_sync_event'],
  onEvent: () => myController.refreshFromRemote(),
  child: AnimatedBuilder(
    animation: myController,
    builder: (context, _) => ListView(...),
  ),
)

4. Debounce Updates (Optional)

如果多个事件可能连续触发,考虑使用防抖:

bool _isUpdating = false;

EventListenerContainer(
  events: ['batch_update'],
  onEvent: () {
    if (_isUpdating) return;
    _isUpdating = true;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _isUpdating = false;
      setState(() {});
    });
  },
  child: MyContentWidget(),
)

Troubleshooting

事件触发了但 UI 没有更新?

检查:

  1. EventListenerContainer 是否正确包裹了目标 UI
  2. onEvent 回调中是否调用了 setState() 或触发更新的方法

如何获取事件参数?

通过 EventManager.instance.getLatestEvent() 获取:

EventListenerContainer(
  events: ['item_added'],
  onEvent: () {
    final latestEvent = EventManager.instance.getLatestEvent('item_added');
    if (latestEvent != null && latestEvent is ItemEventArgs) {
      print('添加的项目ID: ${latestEvent.itemId}');
    }
    setState(() {});
  },
  child: MyContentWidget(),
)

多个监听器会重复触发吗?

不会。每个 EventListenerContainer 都是独立的订阅者,只会更新各自的子树。

Notes

  • 在 initState 自动订阅,dispose 自动取消订阅
  • Debug 模式下自动打印事件收发日志
  • 多个 EventListenerContainer 独立工作,不会相互干扰
  • 使用中文注释与现有代码库保持一致
  • 使用 flutter analyze 验证代码