AtomGit Flutter 鸿蒙客户端:addListener 的正确使用
addListener只有一行代码但它的生命周期管理决定了你的应用是否会产生内存泄漏。一、核心模式// 三步模式overridevoidinitState(){super.initState();model.addListener(_onDataChanged);// 1. 注册_onDataChanged();// 2. 初始加载}void_onDataChanged(){setState((){datamodel.getAll();// 3. 刷新});}overridevoiddispose(){model.removeListener(_onDataChanged);// 必须解绑super.dispose();}二、E-Brufen 中的实际应用// diary_page.dartoverridevoidinitState(){super.initState();_tabControllerTabController(length:3,vsync:this);widget.moodStorage.addListener(_loadMoods);// ← 建立监听_loadMoods();// ← 首次加载}void_loadMoods(){setState((){_allMoodswidget.moodStorage.getAll();_weekMoodswidget.moodStorage.getByWeek(DateTime.now());});}overridevoiddispose(){widget.moodStorage.removeListener(_loadMoods);// ← 移除监听_tabController.dispose();_noteController.dispose();super.dispose();}★ Insight ─────────────────────────────────────_loadMoods()在initState中被调用了两次——一次作为直接调用来加载初始数据一次作为监听器注册等待后续变化。这是 ChangeNotifier 模式的标准写法监听器只在notifyListeners时触发所以你需要手动调用一次来获取初始数据。─────────────────────────────────────────────────三、数据流全链路用户操作 数据层 UI层 ─────── ─────── ───── 点击保存 → _saveMood() → moodStorage.insert() 写入 Hive notifyListeners() ↓ _loadMoods() 被触发 setState() 三 Tab 同时刷新 ✓四、跨页面的数据同步// home_page.dart — 首页速记Futurevoid_quickCheckIn(BuildContextcontext,MoodTypemood)async{awaitmoodStorage.insert(MoodEntry(...));// ↑ 这会触发 notifyListeners()// ↓ DiaryPage 如果已经打开在导航栈中会收到通知}// diary_page.dart — 监听同一 MoodStorage 实例widget.moodStorage.addListener(_loadMoods);因为 HomePage 和 DiaryPage 共享同一个 MoodStorage 实例首页的写入会自动触发日记页的刷新。五、常见陷阱陷阱 1忘记 removeListener// ❌ 错误内存泄漏overridevoiddispose(){_tabController.dispose();super.dispose();// 忘记 removeListener}后果DiaryPage 被销毁后MoodStorage 仍持有对_loadMoods的引用。下次notifyListeners时如果页面已被 GC →setState在已销毁的 Widget 上调用 → 崩溃如果页面未被 GC → 阻止 GC → 内存泄漏陷阱 2在 build 中 addListener// ❌ 错误重复注册overrideWidgetbuild(BuildContextcontext){widget.moodStorage.addListener(_loadMoods);// 每次 build 都注册return...;}build 可以在一秒内执行多次——这会导致同一个回调被注册几十次。陷阱 3匿名函数导致无法 remove// ❌ 错误无法移除model.addListener((){setState((){...});});// removeListener 需要同一个函数引用// 匿名函数每次都是新对象removeListener 找不到它六、调试技巧// 验证监听器是否正确移除overridevoiddispose(){widget.moodStorage.removeListener(_loadMoods);// 断言moodStorage 的 listener 数量应该减少了super.dispose();}小结addListener/removeListener是 ChangeNotifier 模式的心跳。关键纪律只有一条每一个 addListener 必须对应一个 removeListener而且它们必须使用同一个函数引用。作者简介E-Brufen DevFlutter 鸿蒙开发者专注于跨平台移动应用开发与心理健康数字化,项目地址AtomGit - E-Brufen。