状态管理 provider(数据共享)

285 阅读1分钟

当我们想在多个界面(组件、Widget)共享状态(数据)。可以使用官方推荐的 ‘provider’

InheritedWidget

原生flutter中, InheritedWidget 可以进行状态管理。

image.png

class CounterWidget extends InheritedWidget {
  final int count;
  const CounterWidget({super.key, required super.child, required this.count});
  /// 重写此方法
  @override
  bool updateShouldNotify(covariant CounterWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return oldWidget.count != count;
  }
  /// 返回对象
  static CounterWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }
}

在子组件获取状态

image.png

用法比较麻烦 还是使用官方推荐的 provider

provider

引入到yaml中 ,并 pub get

provider: ^6.0.5

举个例子,app内用户信息想要共享。

用户模型层,继承 ChangeNotifier

class UserData extends ChangeNotifier {
  String name = '';
  int _age = 0;

  int get age {
    return _age;
  }
  
  set age(int age) {
    _age = age;
    notifyListeners();
  }
}
class UserData with ChangeNotifier {
  String name = '';
  int _age = 0;

  int get age {
    return _age;
  }

  set age(int age) {
    _age = age;
    notifyListeners();
  }
}

extends 和 with 好像都可以 以后再来解释这个吧。

在设置age的时候 触发。

顶部的Widget配置

单个数据

runApp(
  ChangeNotifierProvider(create: (BuildContext context) {
    return UserData();
  }, child: const MyApp(),),
);

多个数据共享

runApp(MultiProvider(
  providers: [
    ChangeNotifierProvider(
      create: (context) {
        return UserData();
      },
    ),
    ChangeNotifierProvider(create: (context) {
      return AppConfig();
    },),
  ],
  child: const MyApp(),
));

项目中使用

image.png

获取状态(数据)

  • of
  • Consumer

image.png

触发

根据用户模型层可以发现 只要改变age 或者 config中的value值 即可

UserData share = Provider.of(context);
AppConfig config = Provider.of(context);
onPressed: () {
  // 不需要 setState((){})
  share.age++;
  config.setValue(share.age * share.age);
},

此处省略了 setState((){})

使用细节

点击事件中使用

UserModel userModel = Provider.of(context);
print(userModel.isLogin);

报错

======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<UserModel>(context, listen: false);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

The context used was: SliverGrid(delegate: SliverChildBuilderDelegate#2f654(estimated child count: 5), renderObject: RenderSliverGrid#46fac relayoutBoundary=up2)
'package:provider/src/provider.dart':
Failed assertion: line 274 pos 7: 'context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate'

修改成

UserModel userModel = Provider.of(context, listen: false);