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)的集成,以满足更复杂的应用程序需求。

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

基本使用

安装

flutter pub add provider

我们以数字新增为例

1、创建一个状态管理类

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

counter_state.dart

import 'package:flutter/cupertino.dart';

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

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

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

注意点:

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

2、创建顶层共享数据

//使用箭头函数简写
main() {
  runApp(const MyApp());
}

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

或者

runApp(ChangeNotifierProvider(
  create: (context) => CounterState(),
  child: const MyApp(),
));

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

3、在子页面中使用

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

  
  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(context);来获取实例。这里需要注意不要在build方法里执行一些操作,比如数据请求。原因是:调用Provider.of(context); 会重新运行其 build方法,这会导致build里面的某些操作又被执行了一次。,这就是副作用

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

或者

class YcHomePage extends StatelessWidget {
  const YcHomePage({Key? key}) : super(key: key);

  
  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

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

关于获取到对应的状态实例,你可以使用final counter = Provider.of(context);,也可以使用
Consumer(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,其他略,可自行百度

创建自定义状态类

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

使用

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

你可能感兴趣的:(dart,和,Flutter,flutter)