观察者模式 (Observer) - Flutter 的心脏跳动,全靠它传递消息!

setState() 怎么更新界面?Stream 怎么推送数据?背后都是这位“情报站长”在忙活!


想象一下这个场景:

你是个忙碌的店长 📱,管理着你的奶茶店帝国。你有一个超级重要的任务:每当店里发生大事(比如新奶茶配方到货、今日优惠更新、VIP顾客进店),你需要立刻通知所有相关人员!

  • 前台点单员需要知道新配方来做奶茶。
  • 营销专员需要知道优惠信息来更新海报。
  • 店长助理需要知道 VIP 进店来准备特殊服务。

店长不可能每次事件都亲自跑去挨个通知每个人,效率太低,而且店长可能正在忙其他事!

解决方案?建立一个“情报广播站”:

  1. 店长 (Subject / Observable): 他是事件的源头(比如“新配方到货”)。
  2. 情报广播站 (Notification Center): 负责管理和分发消息。
  3. 员工们 (Observers): 点单员、营销专员、店长助理。他们对特定事件感兴趣,提前向广播站登记(订阅) 说:“老板,有新品到货时请务必通知我!”
  4. 事件发生: 当店长(事件源)有新配方到货时,他不用管谁要听,只需告诉广播站:“广播!新配方到货了!”
  5. 自动通知: 广播站立刻查找所有订阅了“新配方到货”通知的员工,挨个给他们发消息:“嘿,点单员A、点单员B、营销专员C,新配方来了,快处理!”
  6. 员工行动: 收到消息的员工根据消息内容更新自己的工作(点单员更新菜单,营销专员制作海报)。

在 Flutter 世界里,这个“情报广播站”就是“观察者模式”!

观察者模式是啥?一句话:

定义对象间的一种一对多的依赖关系,当一个对象(Subject/Observable,被观察者)的状态发生改变时,所有依赖于它的对象(Observers,观察者)都会自动得到通知并更新。

为什么在 Flutter 里它无处不在?

因为 Flutter UI 的核心是 响应式编程!UI 应该是数据的映射。当数据变化时,UI 应该自动更新以反映最新的数据。观察者模式完美地实现了这个“数据变 -> UI 自动变”的机制!

Flutter 中的经典例子:

  1. 最最最核心的 setState()

    • 被观察者 (Subject): State 对象本身(它持有数据,比如 counter)。
    • 观察者 (Observer): build 方法返回的整个 Widget 树(更准确地说,是框架内部管理的机制,它知道哪些 Widget 依赖于 State 的数据)。
    • 过程: 当你调用 setState(() { counter++; }) 时:
      • 你修改了 State 对象的数据 (counter)。
      • setState 内部会标记这个 State 对象为“脏的”(状态改变了)。
      • Flutter 框架(就像那个广播站)在下一帧绘制前,会检查所有“脏”的 State
      • 框架找到对应的 State 对象,通知它:“你的数据变了,需要重建 UI!”
      • State 对象重新调用 build 方法。
      • build 方法用新的 counter 值重建 Widget 树。
      • UI 更新了! 🎉 点单员(UI)知道了新配方(counter值)并更新了菜单(显示的数字)。
    • 本质: setState 触发的重建流程,其底层通知机制就蕴含着观察者模式的思想。Widget 树(或其部分)“观察”着 State 对象的数据变化。
  2. StreamStreamBuilder

    • 被观察者 (Subject): Stream 数据流(比如来自网络请求、传感器、定时器的连续数据)。
    • 观察者 (Observer): StreamBuilder Widget。
    • 过程:
      • 你在 UI 中使用 StreamBuilder,并给它一个 Stream
      • StreamBuilder 订阅 (subscribe) 了这个 Stream(向广播站登记:“有数据来请通知我!”)。
      • Stream 中有新数据 emit(发射)出来时(事件发生)。
      • Stream 内部机制(广播站)通知所有订阅者(StreamBuilder):“新数据来了!”
      • StreamBuilder 收到通知,获取最新的数据,并调用自身的 builder 函数。
      • builder 函数根据新数据重建它负责的那部分 UI。
    • 本质: StreamBuilder 是典型的观察者,它监听 Stream(被观察者)的数据变化并更新 UI。

动手!用观察者模式升级我们的“购物车”

上次我们用单例模式创建了一个全局唯一的购物车管理器 (CartManager)。现在,我们面临一个新问题:当购物车里的商品发生变化(添加、删除)时,如何让 App 里所有显示购物车信息的地方(商品列表页的徽章、购物车页面列表、底部结算栏)都自动刷新?

单例保证了“独一份儿”,但变化如何“广播”出去?—— 观察者模式登场!

在 Flutter 中,实现观察者模式最常用、最轻量级的内置工具是 ChangeNotifierValueNotifier。它们本质上就是“被观察者 (Subject)”。

import 'package:flutter/foundation.dart'; // 引入 ChangeNotifier

// 升级版 CartManager,继承自 ChangeNotifier,让它成为一个"被观察者"
class CartManager with ChangeNotifier {
  // 1. 私有静态实例变量 (单例部分保留)
  static final CartManager _instance = CartManager._internal();

  // 2. 私有构造函数
  CartManager._internal() {
    _items = [];
    print('购物车经理(带广播功能)诞生了!');
  }

  // 3. 公共访问点 (单例部分保留)
  static CartManager get instance => _instance;

  // 4. 购物车商品 (私有)
  List<String> _items = [];

  // 5. 获取商品列表 (只读副本)
  List<String> get items => List.unmodifiable(_items);

  // 6. 添加商品 (关键升级!)
  void addItem(String item) {
    _items.add(item);
    print('添加了: $item');
    // 广播!通知所有观察者:“购物车有变化啦!”
    notifyListeners(); // <----- 观察者模式的核心!
  }

  // 7. 清空购物车 (关键升级!)
  void clearCart() {
    _items.clear();
    print('购物车已清空!');
    // 广播!通知所有观察者:“购物车有变化啦!”
    notifyListeners(); // <----- 观察者模式的核心!
  }
}

升级点解析:

  1. with ChangeNotifierCartManager 混入 ChangeNotifier。这个混入提供了被观察者的核心能力:管理观察者列表 (_listeners) 和最重要的 notifyListeners() 方法。
  2. notifyListeners() 这是广播站的核心功能!调用它,ChangeNotifier 就会自动遍历它内部记录的所有观察者 (listeners),并逐个调用它们的回调函数(相当于广播站挨个打电话通知员工)。我们在数据改变的地方(addItemclearCart)调用它。

如何“观察”购物车的变化?使用 ConsumerListenableBuilder

现在 CartManager 具备了广播能力,我们需要让那些关心购物车变化的 UI 部件(观察者)去订阅这个广播。

// 示例 1:在商品列表页,显示购物车商品总数的小徽章 (位于 AppBar 上)
AppBar(
  title: Text('商品列表'),
  actions: [
    // 使用 Consumer 包裹需要响应变化的 Widget
    Consumer<CartManager>(
      builder: (context, cartManager, child) {
        // cartManager 就是我们的单例 CartManager.instance
        int itemCount = cartManager.items.length;
        return Badge(
          label: Text('$itemCount'),
          child: Icon(Icons.shopping_cart),
        );
      },
    ),
  ],
)

// 示例 2:在购物车页面,显示购物车商品列表
class CartPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('我的购物车')),
      body: // 使用 ListenableBuilder 同样可以
        ListenableBuilder(
          listenable: CartManager.instance, // 指定要监听谁 (被观察者)
          builder: (context, child) {
            // 当 CartManager 调用 notifyListeners() 时,这个 builder 会重新运行
            return ListView.builder(
              itemCount: CartManager.instance.items.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(CartManager.instance.items[index]),
                );
              },
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => CartManager.instance.clearCart(),
        child: Icon(Icons.delete),
      ),
    );
  }
}

Consumer / ListenableBuilder 做了什么?(观察者角色)

  1. 订阅 (Subscribe): 当这个 Widget 被构建时,它会自动CartManager.instance(被观察者)注册自己(context 帮它找到了正确的实例),成为其观察者(listener)。相当于点单员向广播站登记:“有购物车变化请通知我这个徽章!”。
  2. 接收通知 (Notification):CartManager 调用 notifyListeners() 时,Consumer/ListenableBuilder 注册的回调函数会被触发。
  3. 重建 UI (Rebuild): 它们的 builder 函数会被重新调用。在这个函数内部,它们读取 CartManager 当前最新的数据(cartManager.items.lengthCartManager.instance.items),并重建它们包裹的那部分 UI(徽章数字、列表项)。
  4. 高效局部刷新: 只有 Consumer / ListenableBuilder 包裹的这部分 Widget 子树会重建,App 的其他部分不受影响!性能杠杠的!🚀

运行效果:

  1. 在商品列表页点击“加入购物车”按钮(调用 CartManager.instance.addItem('珍珠奶茶'))。
  2. CartManageraddItem 方法内部调用 notifyListeners()
  3. 广播发出! 📢
  4. 商品列表页 AppBar 上的 Consumer<CartManager> 立刻收到通知,它的 builder 重新运行,读取最新的 items.length (比如变成1),然后更新徽章显示为 “1”。
  5. 同时,如果你已经在购物车页面: 购物车页面里的 ListenableBuilder 也收到了同样的通知!它的 builder 也重新运行,读取最新的 items 列表(包含‘珍珠奶茶’),然后重建 ListView,显示出新加入的商品。
  6. 两个页面的 UI,同时、自动地更新了! 店长(CartManager)只喊了一声(notifyListeners),所有关心购物车的员工(UI 部件)都同步行动了。

总结:观察者模式就是你的“事件广播网”

  • 核心目标: 实现对象(通常是数据/状态)和依赖它的对象(通常是UI)之间的松耦合通信。数据源变,所有依赖它的UI自动变。
  • 关键角色:
    • Subject / Observable (被观察者): 状态持有者,负责通知变化(如 ChangeNotifier, ValueNotifier, Stream)。在 Flutter 中常用 ChangeNotifier
    • Observer (观察者): 状态依赖者,订阅变化并响应(如 Consumer, ListenableBuilder, StreamBuilder)。
  • Flutter应用:
    • setState() 的底层通知机制。
    • Stream 数据流与 StreamBuilder
    • 状态管理库的核心! Provider (基于 InheritedWidget + ChangeNotifier), Riverpod, BLoC (基于 Stream) 等流行状态管理库,其核心通信模式都是观察者模式的变体或增强。
    • 任何需要一处数据变化,多处UI更新的场景。
  • 优点: 解耦数据与UI;支持广播通知;实现响应式UI;是Flutter状态管理的基石。
  • 注意: 记得在适当的时候取消订阅(例如在 StatefulWidgetdispose 方法中,或用 Consumer 等自动管理的 Widget),防止内存泄漏(想象员工离职了,广播站还给他打电话)。ConsumerListenableBuilder 通常会自动处理订阅的生命周期,比较安全。手动使用 addListener/removeListener 时需要小心。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值