Flutter开发实战:观察者模式(Observer Pattern)

659 阅读5分钟

先来给大家报个道,上周有事,停更了数天,今天复更。

先来复习一下观察者模式:

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象(主题)和观察者之间的一种一对多的依赖关系,当主题的状态发生改变时,所有依赖于它的观察者都会得到通知并自动更新。

观察者模式主要包含以下几个角色:

  1. 主题(Subject):它是被观察的对象。它通常维护一个观察者列表,用于存放所有关注自己的观察者对象。当主题的状态发生改变时,主题会遍历观察者列表,通知所有的观察者。

  2. 观察者(Observer):它定义了一个更新接口,该接口用于在得到主题的改变通知时更新自己。

  3. 具体主题(Concrete Subject):它是实现了主题接口的具体类,包含一些状态,并在状态发生改变时通知所有的观察者。

  4. 具体观察者(Concrete Observer):它是实现了观察者接口的具体类,它维护一个指向具体主题的引用,用于获取具体主题的状态。

观察者模式在实际开发中使用广泛,在 GUI 开发中,当用户的操作(如点击按钮)改变了某个对象的状态时,需要通知并更新其他相关的组件,就可以使用观察者模式。

在 Flutter 中,你可以使用 StreamStreamBuilder 来实现观察者模式,其中 Stream 相当于主题,StreamBuilder 相当于观察者。当你向 Stream 中添加数据时,所有的 StreamBuilder 都会得到通知并自动更新 UI。

本文将介绍如何在 Flutter 中使用Provider自定义实现观察者模式,以处理组件间的状态管理和通信。我们将通过一个具体的例子来演示这个过程:购物车数量更新。在这个例子中,当关注的状态(购物车数量)发生变化时,所有订阅了这个状态的页面都会自动进行更新。

在Flutter中,观察者模式(Observer Pattern)可以用于实现组件间的状态管理和通信。通过观察者模式,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并进行相应的更新操作

示例:

购物车数量更新 假设我们正在开发一个电子商务应用,其中有一个商品详情页面和一个购物车页面。当用户在商品详情页面点击“加入购物车”按钮时,购物车中的商品数量需要实时更新。

  1. 需求描述:
    • 商品详情页面上有一个"加入购物车"按钮。
    • 购物车页面需要实时显示当前购物车中的商品数量。
  2. 开发实现:
    • 创建一个全局的购物车数量状态类(例如CartCount),用于保存购物车中商品的数量。
    • 在商品详情页面中,当用户点击"加入购物车"按钮时,通过调用购物车数量状态类的方法来增加商品数量。
    • 在购物车页面中,使用观察者模式来监听购物车数量状态类的变化,一旦数量发生改变,购物车页面会实时更新显示。

使用Provider来实现:

ChangeNotifier 是 Flutter 提供的一个简单的观察者模式实现。我们可以创建一个扩展 ChangeNotifier 的类来保存状态,然后在状态变化时调用 notifyListeners 方法。Consumer 组件可以监听 ChangeNotifier,并在状态变化时自动重建。

我们可以创建一个 CartModel 类来保存购物车的数量,并提供一个方法来增加数量。当数量增加时,我们调用 notifyListeners 来通知所有的 Consumer

class CartModel extends ChangeNotifier {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }
}

然后,我们可以在商品详情页面中使用 CartModel 来增加数量:

class ProductDetailPage extends StatelessWidget {
  final CartModel cartModel;

  ProductDetailPage({Key? key, required this.cartModel}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Product Detail'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Add to cart'),
          onPressed: () => cartModel.increment(),
        ),
      ),
    );
  }
}

在购物车页面中,我们使用 Consumer 来监听 CartModel,并显示当前的数量:

class CartPage extends StatelessWidget {
  final CartModel cartModel;

  CartPage({Key? key, required this.cartModel}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Cart'),
      ),
      body: Center(
        child: Consumer<CartModel>(
          builder: (context, cart, child) {
            return Text('Cart Count: ${cart.counter}');
          },
        ),
      ),
    );
  }
}

最后,我们在 main 函数中创建 CartModel 的实例,并使用 ChangeNotifierProvider 来为我们的应用提供这个实例:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CartModel(),
      child: MaterialApp(
        home: Builder(
          builder: (context) => ProductDetailPage(
            cartModel: Provider.of<CartModel>(context, listen: false),
          ),
        ),
        routes: {
          '/cart': (context) => CartPage(
                cartModel: Provider.of<CartModel>(context),
              ),
        },
      ),
    ),
  );
}

如何在不使用任何内置的状态管理机制,如 InheritedWidgetProvider 等,创建自己的观察者模式。

首先,我们定义观察者和被观察者的接口:

抽象的 Observer 类和 Observable 类。Observer 类包含一个 update 方法, Observable 包含 addObserverremoveObservernotifyObservers 方法。

abstract class Observer {
  void update();
}

abstract class Observable {
  final List<Observer> _observers = [];

  void addObserver(Observer observer) {
    _observers.add(observer);
  }

  void removeObserver(Observer observer) {
    _observers.remove(observer);
  }

  void notifyObservers() {
    for (final observer in _observers) {
      observer.update();
    }
  }
}

接下来,创建一个 CartModel 类,它实现了 Observable 接口:

class CartModel extends Observable { 
   int _counter = 0; 
   int get counter => _counter; 
   void increment() { 
       _counter++; notifyObservers(); 
   } 
}

CartPage 类,它实现了 Observer 接口:

class CartPage extends StatefulWidget {
  final CartModel cartModel;

  CartPage({Key? key, required this.cartModel}) : super(key: key);

  @override
  _CartPageState createState() => _CartPageState();
}

class _CartPageState extends State<CartPage> implements Observer {
  @override
  void initState() {
    super.initState();
    widget.cartModel.addObserver(this);
  }

  @override
  void dispose() {
    widget.cartModel.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Cart'),
      ),
      body: Center(
        child: Text('Cart Count: ${widget.cartModel.counter}'),
      ),
    );
  }

  @override
  void update() {
    setState(() {});
  }
}

class ProductDetailPage extends StatelessWidget {
  final CartModel cartModel;

  ProductDetailPage({Key? key, required this.cartModel}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Product Detail'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Add to cart'),
          onPressed: () => cartModel.increment(),
        ),
      ),
    );
  }
}


main 函数中,创建 CartModel 的实例,并传递给 CartPage

void main() {
  final cartModel = CartModel();

  runApp(
    MaterialApp(
      home: ProductDetailPage(cartModel: cartModel),
      routes: {
        '/cart': (context) => CartPage(cartModel: cartModel),
      },
    ),
  );
}

在最后这个例子没有使用任何内置的状态管理机制,完全依靠自定义的观察者模式实现状态管理。

使用两种不同的方法演示了同一个需求的实现,包括一个商品详情页和一个购物车页。在商品详情页中,可以通过点击按钮来增加购物车数量,然后在购物车页中查看当前购物车数量。

通过此例,我们可以看到观察者模式在处理状态管理和组件间通信时的优势。使用观察者模式,我们能够实现状态的封装,保证了数据的一致性,同时也提高了代码的可维护性和可复用性。

然而,手动实现观察者模式需要编写大量的模板代码,且在处理更复杂的状态管理和通信需求时,可能会变得非常复杂。因此,在实际的开发中,通常会使用一些现有的状态管理库,如 Provider、Riverpod、Mobx 等,这些库提供了更高级的功能和工具,可以更方便地实现观察者模式和状态管理。

希望对您有所帮助谢谢!!!