乐趣区

关于flutter:Flutter-Provider状态管理四种消费者使用分析

文章系列

Flutter Provider 状态治理 — 介绍、类图剖析、根本应用

Flutter Provider 状态治理 — 八种提供者应用剖析

Flutter Provider 状态治理 — 四种消费者应用剖析

Flutter Provider 状态治理 —MVVM 架构实战

视频系列

Flutter Provider 状态治理 — 介绍、类图剖析、根本应用

Flutter Provider 状态治理 — 八种提供者应用剖析

Flutter Provider 状态治理 — 四种消费者应用剖析

Flutter Provider 状态治理 —MVVM 架构实战

源码仓库地址

github 仓库地址

前言

在上一篇文章中咱们对 Provider 的 8 种提供者进行了具体的形容以及用对应的案例阐明他们的区别,那么这一节咱们来聊一聊 Provider 的消费者,如果去优化你的我的项目构造以及它们的应用区别。

Provider.of

Provider.of<T>(context)Provider 为咱们提供的静态方法,当咱们应用该办法去获取值的时候会返回查找到的最近的 T 类型的 provider 给咱们,而且也不会遍历整个组件树,上面咱们看下代码:

第一步:定义模型

咱们定义了一个 CountNotifier1 的模型,前面所有的示例代码将围绕该模型来演示

import 'package:flutter/material.dart';

class CountNotifier1 with ChangeNotifier {

  int count = 0;

  void increment() {
    count++;
    notifyListeners();}
}

第二步:应用程序入口设置

return ChangeNotifierProvider(create: (_) => CountNotifier1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ConsumerExample(),),
);

第三步:应用 Provider.of

这里读取值和点击按钮 + 1 时都是通过 Provider.of<T>() 来获取及应用。

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/consumer_example/count_notifier1.dart';
import 'package:provider/provider.dart';

class ConsumerExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text("ConsumerExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text(Provider.of<CountNotifier1>(context).count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(
              padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: (){Provider.of<CountNotifier1>(context).increment();},
                child: Text("点击加 1"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

谬误日志

当咱们运行代码的时候会提醒一个报错,它提醒说试图从 Widget 树内部监听提供者公开的值,如果要修复能够把 listen 改成 false,这个问题其实是在Provider 4.0.2 后会呈现的,最次要的是它的默认行为就是ture,谬误日志如下:

======== 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<CountNotifier1>(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: ConsumerExample(dependencies: [_InheritedProviderScope<CountNotifier1?>])
'package:provider/src/provider.dart':
Failed assertion: line 276 pos 7: 'context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate'

When the exception was thrown, this was the stack: 
........
====================================================================================================

设置 listen 为 false

Provider.of<CountNotifier1>(context, listen: false).increment();

运行后果

Consumer

Consumber只是在 Widget 中调用了 Prvoider.of,并将其结构实现委托给了结构器,比方咱们常见的Builder,如果你的Widget 依赖多个模型,那么它还提供了 Consumer23456 不便调用,咱们接下来对下面的案例采纳 Consumer 来批改

用 Consumer 包裹组件

外面有个 builder 结构器,当咱们把 body 改成上面从新运行后能够发现和应用 Provider.of 的后果一样,然而这里不须要在像应用 Provider.of 那样每次应用都要写一大串的重复性代码。

外面有三个属性:

  • context: 以后的上下文
  • Provider.of<T>(context): 模型对象
  • child: 子组件(不须要刷新的局部)
body: Consumer(builder: (_, CountNotifier1 countNotifier1, child) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [Text(countNotifier1.count.toString(),
               style: TextStyle(
                 color: Colors.red,
                 fontSize: 50
               ),
              ),
          Padding(
            padding: EdgeInsets.only(top: 20),
            child: ElevatedButton(onPressed: (){countNotifier1.increment();
              },
              child: Text("点击加 1"),
            ),
          )
        ],
      ),
    );
  },
),

优化 Consumer

优化形式一:尽可能调整 Consumer 的地位

咱们在下面的代码中发现 Center 以及 Column 组件也被 Consumer 包裹了进来,然而这两个组件是不须要更新状态的,而咱们每次构建的 Widget 的时候,会重建整个body,所以咱们优化一下代码构造,看起来就像上面这样:

body: Center(
  child: Consumer(builder: (_, CountNotifier1 countNotifier1, child) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(countNotifier1.count.toString(),
              style: TextStyle(color: Colors.red, fontSize: 50),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () {countNotifier1.increment();
                },
                child: Text("点击加 1"),
              ),
            ),
                   Container(
              child: Column(
                children: [Text("更多组件 1"),
                  Text("更多组件 2"),
                  Text("更多组件 3"),
                  Text("更多组件 4"),
                  Text("更多组件 5"),
                  Text("更多组件 6"),
                ],
              ),
            )
          ],
        ),
      );
    },
  )
)

优化形式二:不须要刷新但被 Consumer 包裹的组件用 child

比方下面咱们有更多组件 1 - 6 甚至数百个组件无需刷新状态,但因为你用 Consumer 包裹会导致全副刷新,那么显著会导致性能的降落,你可能会想到独自用多个 Consumer 包裹须要刷新的组件就解决了,但这不就是轻重倒置了吗,自身 Provider 是解决代码的 强壮 反复 的代码,所以这个时候咱们能够采纳 Consumer 为咱们提供的 child 参数,如下:

body: Center(
  child: Consumer(builder: (_, CountNotifier1 countNotifier1, child) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(countNotifier1.count.toString(),
              style: TextStyle(color: Colors.red, fontSize: 50),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () {countNotifier1.increment();
                },
                child: Text("点击加 1"),
              ),
            ),
            child!
          ],
        ),
      );
    },
    child: Container(
      child: Column(
        children: [Text("更多组件 1"),
          Text("更多组件 2"),
          Text("更多组件 3"),
          Text("更多组件 4"),
          Text("更多组件 5"),
          Text("更多组件 6"),
        ],
      ),
    ),
  )
),

Selector

Selector类和 Consumer 相似,只是对 build 调用 Widget 办法时提供更精密的管制,简略点来说,Selector也是一个消费者,它容许你能够从模型中筹备定义哪些属性。

咱们来举个例子:

比方,用户模型中有 50 个属性,然而我只须要更新年龄,这样我心愿不须要重建 用户名 电话号码 等组件,那么 Selector 就是用于解决这个问题,咱们看一下示例:

第一步:定义模型

import 'package:flutter/material.dart';

class UserModel6 with ChangeNotifier {

  String name = "Jimi";
  int age = 18;
  String phone = "18888888888";


  void increaseAge() {
    age++;
    notifyListeners();}
}

第二步:应用程序入口设置

return ChangeNotifierProvider(create: (_) => UserModel6(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: SelectorExample(),),
);

第三步:应用 Selector 更精密的管制

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/selector_example/user_model6.dart';
import 'package:provider/provider.dart';

class SelectorExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("SelectorExample"),
      ),
      body: Center(
        child: Selector<UserModel6, int>(selector: (_, userModel6) => userModel6.age,
          builder: (_, age, child) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [Text(age.toString(),
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                ),
                child!
              ],
            );
          },
          child: Padding(padding: EdgeInsets.all(20),
            child: ElevatedButton(onPressed: (){Provider.of<UserModel6>(context, listen: false).increaseAge();},
              child: Text("扭转年龄"),
            ),
          ),
        ),
      ),
    );
  }
}

运行后果

InheritedContext

InheritedContextProvider 内置扩大了 BuildContext,它不保留了组件在树中本人地位的援用,咱们在下面的案例中见到Provider.of<CountNotifier1>(context,listen: false),其实这个of 办法就是应用 Flutter 查找树并找到 Provider 子类型为 CountNotifier1 而已。

三大形式:

  • BuildContext.read: BuildContext.read<CountNotifier1>()能够替换掉 Provider.of<CountNotifier1>(context,listen: false),它会找到CountNotifier1 并返回它。
  • BuildContext.watch: BuildContext.watch<CountNotifier1>()能够替换掉 Provider.of<CountNotifier1>(context,listen: false),看起来和read 没有什么不同,然而应用 watch 你就不须要在应用Consumer
  • BuildContext.select: BuildContext.select<CountNotifier1>()能够替换掉 Provider.of<CountNotifier1>(context,listen: false),看起来和watch 也没有什么不同,然而应用 select 你就不须要在应用Selector

BuildContext.read

上面两种应用形式后果是一样的

应用 Provider.of<T>()

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';


class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),

      /// Provider.of 获取值
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text(Provider.of<CountNotifier2>(context).count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                child: Text("点击加 1"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

应用 BuildContext.read

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';


class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),
      /// read 获取值
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text(context.read<CountNotifier2>().count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                child: Text("点击加 1"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

BuildContext.watch

应用 Consumer

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';

class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),
      /// Consumer 获取值
      body: Center(
        child: Consumer<CountNotifier2>(builder: (_, countNotifier2, child) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [Text(countNotifier2.count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(onPressed: () => countNotifier2.increment(),
                    child: Text("点击加 1"),
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

应用 BuildContext.watch

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';


class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    /// 重要
    final countNotifier2 = context.watch<CountNotifier2>();

    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),
      /// watch 
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text(countNotifier2.count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () => countNotifier2.increment(),
                child: Text("点击加 1"),
              ),
            ),
          ],
        ),
      ),

    );
  }
}

BuildContext.select

应用 Selector

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';

class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),
      /// Selector
      body: Center(
        child: Selector<CountNotifier2, int>(selector: (_, countNotifier2) => countNotifier2.count,
          builder: (_, count, child) {
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [Text(count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                child!
              ],
            );
          },
          child: Padding(padding: EdgeInsets.only(top: 20),
            child: ElevatedButton(onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
              child: Text("点击加 1"),
            ),
          ),
        ),
      ),

    );
  }
}

应用 BuildContext.select

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
import 'package:provider/provider.dart';


class InheritedContextExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    
    /// 重要
    final count = context.select((CountNotifier2 countNotifier2) => countNotifier2.count);

    return Scaffold(
      appBar: AppBar(title: Text("InheritedContextExample"),
      ),
      /// select
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text(count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(padding: EdgeInsets.only(top: 20),
              child: ElevatedButton(onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                child: Text("点击加 1"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

总结

Flutter为咱们提供了多种读取值的形式,下面咱们对消费者四大类的一个应用和剖析比照,大家可依据本人的理论利用场景去应用对应的形式。

退出移动版