Flutter的状态管理之Provider

前言

像vue、react有对应的状态管理库,比如:piniaRedux 。同样flutter中也有状态管理库,但是flutter中的状态管理库很多,对于像我这样的新手来说这很难选择。因此只好选择官方库------Provider

因为是第一次学习,如果有哪些地方理解的不正确,麻烦指出。

参考:

官方文档

Provider

简介

Provider是一个Flutter状态管理库,它是基于InheritedWidget的简单、可扩展且易于使用的解决方案。它帮助开发者在Flutter应用程序中管理和共享状态,以便于组件之间的通信和数据共享。

Provider的核心概念是"Provider"和"Consumer"。Provider是一个数据源,它包含要共享的数据,并将其提供给应用程序中的其他组件。Consumer是一个接收Provider数据的组件,它会自动订阅Provider,并在数据发生变化时进行更新。

使用Provider,开发者可以避免在组件树中手动传递数据。相反,它提供了一种便捷的方式来访问和更新共享数据。开发者只需要在应用程序的根部分创建一个Provider,并在需要访问共享数据的组件中使用Consumer来获取数据。

Provider还提供了一些高级功能,如多Provider的组合、跨组件的数据传递、数据变更通知、异步数据处理等。它还支持与其他状态管理库(如Redux)的集成,以满足更复杂的应用程序需求。

这里的话,我们只学习最基础的部分。高级功能等水平达到后,再学习

基本使用

安装

dart 复制代码
flutter pub add provider

我们以数字新增为例

1、创建一个状态管理类

首先需要创建一个状态管理类,对于需要共享的状态进行统一管理

counter_state.dart

dart 复制代码
import 'package:flutter/cupertino.dart';

// 创建状态管理类
class CounterState with ChangeNotifier {
//   要共享的状态
  int _count = 0;

// 获取状态
  int get count => _count;

// 修改状态的方法
  void increment() {
    _count++;
    //  通知使用该状态的widget
    notifyListeners();
  }
}

注意点:

  • 在Flutter项目中,通常使用小写字母和下划线来命名源代码文件
  • 状态建议定义为私有类型(变量前面加_),不要直接修改状态
  • 使用混合模式,而不是使用继承。避免继承关系的复杂性

2、创建顶层共享数据

dart 复制代码
//使用箭头函数简写
main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  //创建widget的唯一标识
  const MyApp({Key? key}) : super(key: key);
  //重写build方法
  @override
  Widget build(BuildContext context) {
    // ChangeNotifierProvider 会返回一个 ChangeNotifier 对象,它允许消费者在 CounterState 对象发生变化时收到通知。
    return ChangeNotifierProvider(
      // CounterState 对象会作为共享的状态实例被整个应用程序使用。
      create: (context) => CounterState(),
      child: const MaterialApp(
        home: YcHomePage(),
      ),
    );
  }
}

或者

dart 复制代码
runApp(ChangeNotifierProvider(
  create: (context) => CounterState(),
  child: const MyApp(),
));

本质上是通过ChangeNotifierProvider来为最父级的widget来提供状态

3、在子页面中使用

dart 复制代码
//app的主页面
class YcHomePage extends StatelessWidget {
  const YcHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 从上下文中获取类型为 CounterState 的对象
    final counter = Provider.of<CounterState>(context);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Provider状态管理'),
      ),
      body: Center(
          child: Column(
        children: [
          // 调用get方法获取值
          Text("数值是:${counter.count}"),
          // 用于占位
          const SizedBox(
            height: 30,
          ),
          ElevatedButton(
              onPressed: () {
                //  调用increment方法
                counter.increment();
              },
              child: const Text("加1"))
        ],
      )),
    );
  }
}

注意点:

避免产生副作用,我们在build方法里通过 final counter = Provider.of<CounterState>(context);来获取实例。这里需要注意不要在build方法里执行一些操作,比如数据请求。原因是:调用Provider.of<T>(context); 会重新运行其 build方法,这会导致build里面的某些操作又被执行了一次。,这就是副作用

解决方式是在initState方法里来执行这些操作,initState 只会在初始化的时候被执行。

或者

dart 复制代码
class YcHomePage extends StatelessWidget {
  const YcHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 掉接口获取数据,或者其他的操作
    return Scaffold(
      appBar: AppBar(
        title: const Text('Provider状态管理'),
      ),
      body: Center(
          child: Consumer<CounterState>(
            builder: (context,counterState,child){
              return Column(
                children: [
                  // 调用get方法获取值
                  Text("数值是:${counterState.count}"),
                  // 用于占位
                  const SizedBox(
                    height: 30,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        //  调用increment方法
                        counterState.increment();
                      },
                      child: const Text("加1"))
                ],
              );
            }
          )
      )
    );
  }
}

这种方式是通过Consumer来创建一个消费者,然后在builder函数中通过上下文参数(context)来获取CounterState对象。这样做的优点是可以直接在builder函数中访问CounterState对象,并且可以在builder函数中进行一些额外的逻辑处理。

这种方式下,在build里执行的操作不会在状态更新后再次执行。但是在builder中的话,当状态更新后每次都会执行。这里可以自己使用print打印一下日志试试。

局部使用

局部使用与全局使用很相似,假设有两个子组件需要共享某个状态,这时你可以在这两个子组件的公共父组件上使用ChangeNotifierProvider来提供状态。

多个 Provider

如果需要共享的状态不多时,使用一个状态管理类是没有问题的。但是当状态过多时,都放在一个状态管理类里是不合适。这时应该建立不同的状态类,来管理不同类别的状态。这时后可以结合MultiProvider

dart 复制代码
main() {
  runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider<CounterState>(create: (context) => CounterState()),
      //  其他的状态
    ],
    child: const MyApp(),
  ));
}

关于获取到对应的状态实例,你可以使用final counter = Provider.of<CounterState>(context);,也可以使用
Consumer<CounterState>(builder: (context, counterState, child) {} ,唯一需要注意的是类型不要填写错误了。

其他类型的Provider

上面的例子中使用的都是ChangeNotifierProvider,除了ChangeNotifierProvider还有其他的Provider

ListenableProvider:

  • ListenableProvider是Flutter中的一种Provider,用于将依赖项注入到需要监听列表数据变化的组件中。
  • 当依赖项发生变化时,ListenableProvider会通知所有订阅者(subscribers)更新他们的UI。
  • 适合在需要监听列表数据变化的情况下使用,例如在列表视图中显示动态数据时,可以使用ListenableProvider来管理数据并提供数据变化的通知。

ChangeNotifierProvider:

  • ChangeNotifierProvider是Dart语言中另一个用于实现依赖注入的Provider类,它主要用于管理可变数据并提供数据变化的通知。
  • 当依赖项发生变化时,ChangeNotifierProvider会通知所有订阅者(subscribers)更新他们的UI。
  • 适合在需要管理可变数据并提供数据变化通知的情况下使用,例如在应用程序中管理用户设置或配置信息时,可以使用ChangeNotifierProvider来提供数据变化通知并更新相关UI。

ValueListenableProvider:

  • ValueListenableProvider是Flutter中的一种Provider类,用于将依赖项注入到需要监听单个值变化或状态改变的组件中。
  • 当依赖项发生变化时,ValueListenableProvider会通知所有订阅者(subscribers)更新他们的UI。
  • 适合在需要监听单个值或状态改变的情况下使用,例如在应用程序中管理用户状态或设置时,可以使用ValueListenableProvider来提供值变化通知并更新相关UI。

StreamProvider:

  • StreamProvider是Dart语言中的一种Provider类,用于将依赖项注入到需要监听流数据变化的组件中。
  • 当依赖项发生变化时,StreamProvider会通知所有订阅者(subscribers)更新他们的UI。
  • 适合在需要监听流数据变化的情况下使用,例如在实时更新数据流的情况下,可以使用StreamProvider来管理流数据并提供数据变化的通知。

下面简单看一个ListenableProvider,其他略,可自行百度

创建自定义状态类

dart 复制代码
class ListDataState with ChangeNotifier {
  List<String> _listData = [];

  List<String> get listData => _listData;

  void addItem(String item) {
    _listData.add(item);
    notifyListeners();
  }

  void removeItem(String item) {
    _listData.remove(item);
    notifyListeners();
  }
}

使用

dart 复制代码
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('列表数据状态管理'),
    ),
    body: Consumer<ListDataState>(
      builder: (context, listDataState, child) {
        return ListView.builder(
          itemCount: listDataState.listData.length,
          itemBuilder: (context, index) {
            final item = listDataState.listData[index];
            return ListTile(
              title: Text(item),
              onTap: () {
                listDataState.removeItem(item);
              },
            );
          },
        );
      },
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () {
        Provider.of<ListDataState>(context, listen: false).addItem('New Item');
      },
      child: Icon(Icons.add),
    ),
  );
}
相关推荐
火柴就是我8 小时前
flutter 之真手势冲突处理
android·flutter
Speed1238 小时前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间8 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭9 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone9 小时前
从flutter源码看其渲染机制
android·flutter
ALLIN1 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei1 天前
Flutter 国际化
flutter
Dabei1 天前
Flutter MQTT 通信文档
flutter
Dabei1 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉1 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter