Flutter之状态管理(一) Provider

Provider

Provider 为我们提供了一种简单、低样板的方法,将业务逻辑与应用程序中的小部件分开。由于它是基于InheritedWidget类构建的,因此还可以轻松地重用和重构业务逻辑。将状态与 UI 分离是 Provider 解决的主要问题之一。

Provider能为我们做什么? 

  • 将状态和UI分开
  • 根据状态的变化重建UI

步骤:

添加依赖项:

找到 pubspec.yaml 文件:

dependencies:
    provider: ^6.0.5

 导入包:import 'package:provider/provider.dart';

ChangeNotifier

ChangeNotifier is a simple class included in the Flutter SDK which provides change notification to its listeners. In other words, if something is a ChangeNotifier, you can subscribe to its changes. (It is a form of Observable, for those familiar with the term.)

ChangeNotifier是 Flutter SDK 中包含的一个简单类,它向其侦听器提供更改通知。换句话说,如果某件事是 a ChangeNotifier,您可以订阅它的更改。(对于熟悉该术语的人来说,它是 Observable 的一种形式。)

In providerChangeNotifier is one way to encapsulate your application state. For very simple apps, you get by with a single ChangeNotifier. In complex ones, you’ll have several models, and therefore several ChangeNotifiers. (You don’t need to use ChangeNotifier with provider at all, but it’s an easy class to work with.)

provider中ChangeNotifier是封装应用程序状态的一种方法。对于非常简单的应用程序,您可以使用单个ChangeNotifier. 在复杂的模型中,您将有多个模型,因此也有多个 ChangeNotifiers. (您根本不需要使用ChangeNotifierwith provider ,但它是一个易于使用的类。)

个人理解:ChangeNotifier是真正存放数据的地方

//真正的数据存放处
class favorite extends ChangeNotifier{

  bool isfavorite=false;

  void modify(){
    isfavorite=!isfavorite;
    notifyListeners();
  }
}

class food extends ChangeNotifier{
  List<String> foods=["苹果","李子","桃子","橙子"];

  void addfood(String food){
    foods.add(food);
    notifyListeners();
  }
}

ChangeNotifierProvider

ChangeNotifierProvider is the widget that provides an instance of a ChangeNotifier to its descendants. It comes from the provider package.

We already know where to put ChangeNotifierProvider: above the widgets that need to access it. In the case of CartModel, that means somewhere above both MyCart and MyCatalog.

You don’t want to place ChangeNotifierProvider higher than necessary (because you don’t want to pollute the scope). But in our case, the only widget that is on top of both MyCart and MyCatalog is MyApp.

 It also automatically calls dispose() on CartModel when the instance is no longer needed.

ChangeNotifierProvider 是一个向其后代提供 ChangeNotifier 实例的小部件。 它来自提供程序包。

我们已经知道将 ChangeNotifierProvider: 放在需要访问它的小部件之上的位置。 就 CartModel 而言,这意味着位于 MyCart 和 MyCatalog 之上。

您不想将 ChangeNotifierProvider 放置在高于必要的位置(因为您不想污染范围)。 但在我们的例子中,MyCart 和 MyCatalog 之上的唯一小部件是 MyApp。

当在CarModel中不再需要时,它会自动调用dispose()方法

个人理解:通过创建ChangeNotifierProvider 将真正存放数据的ChangeNotifier提供给需要的widget

使用单个Provider: 

main()=>runApp(
  ChangeNotifierProvider(
      create: (_)=>favorite(),
      child: MyApp(),
  )
);

多个Provider:

main()=>runApp(
  MultiProvider(providers: [
    ChangeNotifierProvider(create: (_)=>favorite()),
    ChangeNotifierProvider(create: (_)=>food(),),
  ],
    child: MyApp(),
  )
);

使用数据

  • Consumer

Now that CartModel is provided to widgets in our app through the ChangeNotifierProvider declaration at the top, we can start using it.

现在通过ChangeNotifierProvider 在顶部的声明,CartModel 已经可以被提供给app中的widget了

return Consumer<CartModel>(
  builder: (context, cart, child) {
    return Text('Total price: ${cart.totalPrice}');
  },
);
  •  必须指定要访问的模型的类型,如:Consumer<CartModel>
  • Consumer中唯一必须的参数就是builder,Builder 是一个每当发生变化时就会被调用的函数 ChangeNotifier。(换句话说,当您调用notifyListeners() 模型时,所有相应 Consumer小部件的所有构建器方法都会被调用。
  • builder需要三个参数,
  1. context,每个build方法都会有上下文,目的是知道当前树的位置
  2. ChangeNotifier对应的实例,也是我们在builder函数中主要使用的对象
  3. child,目的是进行优化,如果builder下面有一颗庞大的子树,当模型发生改变的时候,我们并不希望重新build这颗子树,那么就可以将这颗子树放到Consumer的child中,在这里直接引入即可
class demo2Consumer extends StatelessWidget {
  const demo2Consumer({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<favorite>(
      builder: (context,instancs,child){
        return  Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("喜欢1"),
            SizedBox(width: 20,),
            InkWell(
              onTap: (){
                print("添加到喜欢");
                instancs.modify();
                print("当前是否喜欢:${context.read<favorite>().isfavorite}");
              },
              child:instancs.isfavorite?
              Icon(Icons.favorite,color: Colors.red,):
              Icon(Icons.favorite_border_rounded,),
            ),
            SizedBox(width: 20,),
            Text("当前是否喜欢:  ${context.read<favorite>().isfavorite}")
          ],
        );
      },
    );
  }
}
  • Provider.of

Provider.of<T>(context) 是 Provider 为我们提供的静态方法,当我们使用该方法去获取值的时候会返回查找到的最近的 T 类型的 provider 给我们,且也不会遍历整个组件树。 

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

  @override
  Widget build(BuildContext context) {
    final demo=Provider.of<favorite>(context);
    return  Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text("喜欢2"),
        SizedBox(width: 20,),
        InkWell(
          onTap: (){
            demo.modify();
          },
          child: demo.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
        ),
        SizedBox(width: 20,),
        Text("当前是否喜欢:  ${context.read<favorite>().isfavorite}")
      ],
    );
  }
}
  • Selector

Selector相当于 Consumer 可以通过选择有限数量的值来过滤更新,并在它们没有更改时防止重建。

源码:

即从 A 类型的数据中选择 S 类型数据。然后,这个返回值被传递给父类构造函数中的 selector 参数。

class Selector<A, S> extends Selector0<S> {
  /// {@macro provider.selector}
  Selector({
    Key? key,
    required ValueWidgetBuilder<S> builder,
    required S Function(BuildContext, A) selector,//传过来一个A,返回一个S
    ShouldRebuild<S>? shouldRebuild,
    Widget? child,
  }) : super(
          key: key,
          shouldRebuild: shouldRebuild,
          builder: builder,
          selector: (context) => selector(context, Provider.of(context)),
          child: child,
        );
}
required S Function(BuildContext, A) selector

selector 是一个回调函数,它接受两个参数:一个是 BuildContext 类型的 context,另一个是泛型类型 A。这个回调函数的返回值是泛型类型 S

selector: (context) => selector(context, Provider.of(context)),

 selector 函数被调用,并传入了 context 和 Provider.of(context) 两个参数。这个表达式的返回值是 selector(context, Provider.of(context)),其中Provider.of(context)),即从 A 类型的数据中选择 S 类型数据。然后,这个返回值被传递给父类构造函数中的 selector 参数,该selector回调的任务是返回一个对象,该对象仅包含builder所需的信息。

在 Selector 类的定义中,builder 参数的类型为 ValueWidgetBuilder<S>。

它是一个回调函数,它接受三个参数:BuildContext 类型的 context、泛型类型 S 的数据和一个可选的 Widget 类型的子部件。它的返回值是一个小部件,通常是根据 S 类型的数据构建的。

typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, Widget? child);

例子: 

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

  @override
  Widget build(BuildContext context) {
    return Selector<favorite,bool>(
      selector:(context,favoritedemo)=>favoritedemo.isfavorite,
      shouldRebuild: (pre,next)=>true,
      builder: (context,demo1,child){
        return  Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(width:150,child:Text("喜欢Selectorbool"),),
            SizedBox(width: 20,),
            Text("当前是否喜欢:  ${demo1}")
          ],
        );
      }, );
  }
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

main()=>runApp(
  MultiProvider(providers: [
    ChangeNotifierProvider(create: (_)=>favorite()),
    ChangeNotifierProvider(create: (_)=>food(),),
  ],
    child: MyApp(),
  )
);

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "收藏",
      home: HYHomePage(),
    );
  }
}
//消费者 Provider.of(context)
class HYHomePage extends StatelessWidget {

  const HYHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("收藏示例"),
      ),
      body: Center(
        child:Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            demo1Provider(),
            SizedBox(height: 20,),
            demo2Consumer(),
            SizedBox(height: 20,),
            demo3Selector(),
            SizedBox(height: 20,),
            demo4Selector(),
          ],
        ) ,
      ),
    );
  }
}
/*Provider.of<T>(context) 是 Provider 为我们提供的静态方法,
当我们使用该方法去获取值的时候会返回查找到的最近的 T 类型的 provider 给我们,且也不会遍历整个组件树。*/
class demo1Provider extends StatelessWidget {
  const demo1Provider({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final demo=Provider.of<favorite>(context);
    final demo2=Provider.of<food>(context);
    return  Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Container(width:100,child: Text("喜欢Provider")),
        SizedBox(width: 20,),
        InkWell(
          onTap: (){
            demo.modify();
            demo2.addfood("一只猫猫爪");
          },
          child: demo.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
        ),
        SizedBox(width: 20,),
        Text("当前是否喜欢:  ${context.read<favorite>().isfavorite}"),
        SizedBox(width: 20,),
        Text("${demo2.foods}"),
      ],
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Consumer2<favorite,food>(
      builder: (context,instancs,foodinstanc,child){
        return  Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(width:100,child:Text("喜欢Consumer"),),
            SizedBox(width: 20,),
            InkWell(
              onTap: (){
                print("添加到喜欢");
                instancs.modify();
                foodinstanc.addfood("大芒果");
                print("当前是否喜欢:${context.read<favorite>().isfavorite}");
              },
              child:instancs.isfavorite?
              Icon(Icons.favorite,color: Colors.red,):
              Icon(Icons.favorite_border_rounded,),
            ),
            SizedBox(width: 20,),
            Text("当前是否喜欢:  ${context.read<favorite>().isfavorite}"),
            SizedBox(width: 20,),
            Text("${foodinstanc.foods}"),
          ],
        );
      },
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final demo22=Provider.of<food>(context);
    return Selector<favorite,favorite>(
      selector:(context,favoritedemo)=>favoritedemo,
      shouldRebuild: (pre,next)=>true,
      builder: (context,demo1,child){
        return  Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(width:100,child:Text("喜欢Selector"),),
              SizedBox(width: 20,),
              InkWell(
                onTap: (){
                  demo1.modify();
                  demo22.addfood("孙悟空最喜欢的桃子");
                },
                child: demo1.isfavorite? Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
              ),
              SizedBox(width: 20,),
              Text("当前是否喜欢:  ${demo1.isfavorite}"),
              SizedBox(width: 20,),
              Text("${Provider.of<food>(context).foods}"),
            ],
          );
        }, );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Selector<favorite,bool>(
      selector:(context,favoritedemo)=>favoritedemo.isfavorite,
      shouldRebuild: (pre,next)=>true,
      builder: (context,demo1,child){
        return  Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(width:150,child:Text("喜欢Selectorbool"),),
            demo1?Icon(Icons.favorite,color: Colors.red,):Icon(Icons.favorite_border_rounded,),
            SizedBox(width: 20,),
            Text("当前是否喜欢:  ${demo1}"),
            SizedBox(width: 20,),
            Text("${Provider.of<food>(context).foods}"),
          ],
        );
      }, );
  }
}
//真正的数据存放处
class favorite extends ChangeNotifier{

  bool isfavorite=false;

  void modify(){
    isfavorite=!isfavorite;
    notifyListeners();
  }
}

class food extends ChangeNotifier{
  List<String> foods=["苹果","李子","桃子","橙子"];

  void addfood(String food){
    foods.add(food);
    notifyListeners();
  }
}

总结:

ChangeNotifier是用于存放数据的地方,ChangeNotifierProvider是用于将数据提供给需要的Widget的地方,而Consumer则是用于使用这些数据的地方。

当数据发生变化时,ChangeNotifier会通知依赖它的Consumer重新构建,并使用新的数据来更新UI。这种方式可以实现局部重建,提高应用的性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flutter状态管理Provider是一种用于管理Flutter应用程序状态的库。它提供了一种简单的方式来管理应用程序的状态,使得开发者可以更加轻松地构建复杂的应用程序。使用Provider,开发者可以轻松地将状态从一个小部件传递到另一个小部件,而无需手动管理状态。这使得代码更加简洁,易于维护。同时,Provider还提供了一些高级功能,如异步状态管理和依赖注入。总之,Provider是一个非常有用的Flutter状态管理库,可以帮助开发者更加轻松地构建高质量的应用程序。 ### 回答2: Flutter 是一种最快速开发应用程序的框架之一。Flutter 中有很多不同的状态管理工具,其中最受欢迎的是 ProviderProvider 是一个包含了多个 Flutter 应用程序的不同状态或数据的管理工具。Provider 使您可以轻松访问和管理应用程序的各个部分中的数据。 Provider 有两个基本要素:一个是 Provider,另一个是 Consumer。Provider 可以包含任何数据,例如字符串、整数、对象、函数等等。而 Consumer 是能够访问这些数据并显示它们的 Widget。 Provider 可以在整个应用程序中共享数据和状态。如果您需要在应用程序的多个部分中保持数据的同步状态,则可以使用 Provider管理这些数据和状态。在使用 Provider 之前,您需要定义一个类并将其继承自 ChangeNotifier。该类将包含数据和状态,并且还需要通过 notifyListeners() 告知侦听数据的 Consumer。 使用 Provider 的好处是封装状态管理的代码。这样,您不必在应用程序或 Widget 中编写大量的状态管理代码。在使用 Provider 之后,您可以轻松地在应用程序或 Widget 中访问数据和状态。 最后,值得一提的是,Provider 的使用方式很多,您可以使用官方提供的 Provider 包来管理应用程序中的数据和状态,也可以使用其他第三方状态管理工具来实现您的应用程序的功能。在使用任何工具之前,请确保仔细了解它们的优缺点,以便于选择最适合您的应用程序的状态管理工具。 ### 回答3: Flutter是一个非常强大的UI框架,但随着应用规模增大,状态管理变得越来越复杂,导致代码可读性和可维护性变得很难。Flutter状态管理解决方案有多种,其中比较常用的就是ProviderProviderFlutter中一个状态管理框架,它使用InheritedWidget来传递状态,同时具有透明性、灵活性和便捷性。通过Provider,我们可以在不同Widget之间共享相同的数据,从而保证数据在应用中的一致性。 使用Provider的步骤非常简单,首先需要在我们的Flutter项目中引入Flutter Provider库,然后定义我们需要共享的数据模型,并将其包装在Provider中,最后在UI界面中使用数据。 下面是一篇使用Provider实现商品购物车的例子: 1.定义数据模型 ``` class Product { String title; double price; Product({ required this.title, required this.price, }); } ``` 2.创建Provider ``` class CartModel extends ChangeNotifier { final List<Product> _items = []; List<Product> get items => _items; void add(Product product) { _items.add(product); notifyListeners(); } void remove(Product product) { _items.remove(product); notifyListeners(); } } ``` 3.在Widget树中使用Provider ``` return ChangeNotifierProvider( create: (context) => CartModel(), child: MaterialApp( home: Scaffold( body: Center( child: Consumer<CartModel>( builder: (context, cart, child) { return Text('Cart items: ${cart.items.length}'); }, ), ), ), ), ); ``` 在上面的代码中,我们使用ChangeNotifierProvider包装了CartModel,并将其注册到应用的根节点。Consumer在UI界面中使用CartModel的数据,并提供数据发生变化时的回调闭包。 总的来说,Provider能够在Flutter应用中实现更清晰、安全和高效的状态管理,在开发中能够带来更好的用户体验和开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值