Flutter 的 Provider

90 阅读2分钟

1. 自实现Provider

Flutter SDK中提供的ChangeNotifier类 ,它继承自Listenable,也实现了一个Flutter风格的发布者-订阅者模式,ChangeNotifier定义大致如下:


class ChangeNotifier implements Listenable {

  List listeners=[];

  @override

  void addListener(VoidCallback listener) {

     //添加监听器

     listeners.add(listener);

  }

  @override

  void removeListener(VoidCallback listener) {

    //移除监听器

    listeners.remove(listener);

  }

  

  void notifyListeners() {

    //通知所有监听器,触发监听器回调 

    listeners.forEach((item)=>item());

  }

   

  ... //省略无关代码

}

可以通过调用addListener()removeListener()来添加、移除监听器(订阅者);通过调用notifyListeners() 可以触发所有监听器回调。

2. 购物车示例

1.  向购物车中添加新商品时总价更新

定义一个Item类,用于表示商品信息:


class Item {

  Item(this.price, this.count);

  double price; //商品单价

  int count; // 商品份数

  //... 省略其他属性

}

定义一个保存购物车内商品数据的CartModel类:


class CartModel extends ChangeNotifier {

  // 用于保存购物车中商品列表

  final List<Item> _items = [];

  


  // 禁止改变购物车里的商品信息

  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);

  


  // 购物车中商品的总价

  double get totalPrice =>

      _items.fold(0, (value, item) => value + item.count * item.price);

  


  // 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。

  void add(Item item) {

    _items.add(item);

    // 通知监听器(订阅者),重新构建InheritedProvider, 更新状态。

    notifyListeners();

  }

}

CartModel即要跨组件共享的model类


class ProviderRoute extends StatefulWidget {

  @override

  _ProviderRouteState createState() => _ProviderRouteState();

}

  


class _ProviderRouteState extends State<ProviderRoute> {

  @override

  Widget build(BuildContext context) {

    return Center(

      child: ChangeNotifierProvider<CartModel>(

        data: CartModel(),

        child: Builder(builder: (context) {

          return Column(

            children: <Widget>[

              Builder(builder: (context){

                var cart=ChangeNotifierProvider.of<CartModel>(context);

                return Text("总价: ${cart.totalPrice}");

              }),

              Builder(builder: (context){

                print("ElevatedButton build"); //在后面优化部分会用到

                return ElevatedButton(

                  child: Text("添加商品"),

                  onPressed: () {

                    //给购物车中添加商品,添加后总价会更新

                    ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));

                  },

                );

              }),

            ],

          );

        }),

      ),

    );

  }

}

Provider的原理和流程图

image.png

可以发现使用Provider,将会带来如下好处:

1.  业务代码更关注数据了,只要更新Model,则UI会自动更新,而不用在状态改变后再去手动调用setState()来显式更新页面。

1.  数据改变的消息传递被屏蔽了,无需手动去处理状态改变事件的发布和订阅了,这一切都被封装在Provider中了。

1.  在大型复杂应用中,尤其是需要全局共享的状态非常多时,使用Provider将会大大简化代码逻辑,降低出错的概率,提高开发效率。