记录flutter---Provide基于InheritedWidget的数据共享

588 阅读4分钟

对于flutter的数据共享,我是很懵逼的,真的不如vue和react那么直观明了,果然还是编程思想不够啊,通过根据flutter的官网介绍以及自己超的官网代码有了一点点理解吧。毕竟刚接触,极大的可能理解的不对。。。。。。

image.png

1、创建一个类,用于全局保存数据

创建InheritedProvider类后,然后继承flutter自带的数据共享类InheritedWidget,这个类和vue里面的祖孙之间传递数据(provide/inject)的概念是一样的,属于隔代遗传。它接受两个参数,一个是需要保持数据的组件,一个是需要保存的数据,也就是传递组件中的数据。。这点我都看不上它,vue中保存数据直接存在store中,然而此方式是将数据传递给该数据,要是说非要有点关系,也就是vue中通过组件将数据commit到store这点相似。。。。或许flutter也能直接讲全局常量存在该类中,但我还没了解到。。。。。

这个是通过继承InheritedWidget类直接实现数据共享

image.png

import 'package:flutter/material.dart';
//用于保存数据共享的数据以及方法
class InheritedProvider<T> extends InheritedWidget {
  InheritedProvider({
    required Widget child,
    required this.data,
  }) : super(child: child);
  final T data;
  @override
  bool updateShouldNotify(InheritedProvider<T> old) {
    return true;
  }
}

2、再创建一个用于通知全局数据改变的类ChangeNotifierProvider

这个类,我觉得它才是重点,继承了上了一类,而且提供了能拿到传递过来的数据的方法of,还提供了对传递数据进行了对比的方法,监听是否更新,既然继承了,说明后期咱们都用不到了InheritedProvider类,只需要往ChangeNotifierProvider类中传递数据和组件就行了,要是那个组件需要点用共享数据,直接饮用该类中of方法即可,上代码。。。。。。看不懂吗????,慢慢啃,我也不懂。。。

import 'package:demo/pages/view/provider/InheritedProvider.dart';
import 'package:flutter/material.dart';
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
  ChangeNotifierProvider({
    Key? key,
    required this.data,
    required this.child,
  });
  final Widget child;
  final T data;
  //定义一个便捷方法,方便子树中的widget获取共享数据
  static T of<T>(BuildContext context) {
    // final type = _typeOf<InheritedProvider<T>>();
    final provider =
        context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();
    return provider!.data;
  }

  @override
  _ChangeNotifierProviderState<T> createState() =>
      _ChangeNotifierProviderState<T>();
}

class _ChangeNotifierProviderState<T extends ChangeNotifier>
    extends State<ChangeNotifierProvider<T>> {
  void update() {
    //如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
    setState(() => {});
  }

  @override
  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
    //当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
    if (widget.data != oldWidget.data) {
      oldWidget.data.removeListener(update);
      widget.data.addListener(update);
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  void initState() {
    // 给model添加监听器
    widget.data.addListener(update);
    super.initState();
  }

  @override
  void dispose() {
    // 移除model的监听器
    widget.data.removeListener(update);
    super.dispose();
  }
   //讲数据传递给全局的数据共享
  @override
  Widget build(BuildContext context) {
    return InheritedProvider<T>(
      data: widget.data,
      child: widget.child,
    );
  }
}

放着不要动,之后就是给它传递数据和组件了。。。。。。下面创建一个例子

3、创建一个管理主页面的数据以及方法CartModel类

该方法里面创建了一个实例,变量为单价和数量,这个功能就是,点击添加数量,总价格也会相应的改变,其实正常写哪有这么多*事,直接写就完了,可是咱们不是要用将这个model类传递给共享嘛,迎着头皮写吧,没办法。。。。。,

import 'dart:collection';
import 'package:flutter/material.dart';

class Item {
  Item(this.price, this.count);
  double price; //商品单价
  int count; // 商品数量
}

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();
  }
}

fold方法数据集合元素的折叠也可作为循环使用

4、剩下就是页面中怎么传递数据以使用了

看下面的引入包,只需要引入通知类和model类,将model作为数据传递给ChangeNotifierProvider,然后通过of函数拿到里面的数据,

import 'package:flutter/material.dart';
import './model/CartModel.dart';
import './model/ChangeNotifierProvider.dart';

class ProviderRoute extends StatefulWidget {
  @override
  _ProviderRouteState createState() => _ProviderRouteState();
}

class _ProviderRouteState extends State<ProviderRoute> {
  @override
  Widget build(BuildContext context) {
    return Center(
    //传递需要全局共享的数据CartModel
      child: ChangeNotifierProvider<CartModel>(
        data: CartModel(),
        //将子组件传递给ChangeNotifierProvider类需要传递个上下文context
        child: Builder(builder: (context) {
          return Column(
            children: <Widget>[
              Builder(builder: (context) {
              //通过of拿到计算出来的总价格,显示在页面上
                var cart = ChangeNotifierProvider.of<CartModel>(context);
                return Text("总价: ${cart.totalPrice}");
              }),
              Builder(builder: (context) {
                print("ElevatedButton build"); //在后面优化部分会用到
                return ElevatedButton(
                  child: Text("添加商品"),
                  //调用ChangeNotifierProvider中的add,改变数量
                  onPressed: () {
                  
                    //给购物车中添加商品,添加后总价会更新,
                    ChangeNotifierProvider.of<CartModel>(context)
                        .add(Item(20.0, 1));
                  },
                );
              }),
            ],
          );
        }),
      ),
    );
  }
}

通过ChangeNotifierProvider.of(context)调用通过ChangeNotifierProvider中的方法。先到这吧,就只能会用了详细参考