Provider中的watch、read、Consumer、ChangeNotifierProvider、ValueNotifierProvider

一、watch、read、Consumer状态消费API

watchConsumer 都用于监听状态变化并重建 Widget,但 watch 是通过 BuildContext 的扩展方法实现,更简洁,适用于简单场景;Consumer 是一个 Widget,提供了更灵活的构建方式,适用于需要精细控制 Widget 结构的场景。
readwatchConsumer 的主要区别在于它不监听状态变化,用于获取状态对象执行非 UI 相关操作,避免不必要的 Widget 重建。在实际使用中,通常会结合 watchConsumer 以及 read 来实现既高效又灵活的状态管理和 UI 更新。

1.1. watch

功能watch 用于监听状态变化。当被监听的状态对象发生改变时,使用 watch 的 Widget 会自动重建。它内部依赖 InheritedWidget 机制来检测状态变化。

使用场景:适用于需要根据状态变化更新 UI 的场景。例如,在一个计数器应用中,当计数器的值发生变化时,UI 需要显示最新的计数值。

代码示例

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

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Provider watch示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 使用watch获取并监听Counter状态
              Text('计数: ${context.watch<Counter>().count}'),
              ElevatedButton(
                onPressed: () {
                  context.read<Counter>().increment();
                },
                child: Text('增加计数'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在上述代码中,context.watch<Counter>().count 用于获取 Counter 实例并监听其变化。当调用 increment 方法改变计数器的值时,Text 组件会因为状态变化而重建,从而显示最新的计数值。

1.2. read

功能read 用于读取状态对象,但不会监听状态变化。它主要用于在不希望 Widget 因状态变化而重建的情况下获取状态对象,例如执行一些与 UI 更新无关的操作,如业务逻辑处理、数据计算等。

使用场景 :当你需要获取状态对象来执行一些非 UI 相关的操作时,read 是一个合适的选择。例如,在点击按钮增加计数器值的逻辑中,我们只需要获取 Counter 对象来调用 increment 方法,而不需要监听其变化来更新 UI(因为更新 UI 的操作由 watch 负责)。

代码示例 :上述计数器示例中,context.read<Counter>().increment(); 就是使用 read 获取 Counter 对象并调用其 increment 方法,这里不需要监听 Counter 的变化,所以使用 read 而不是 watch

1.3. Consumer

功能Consumer 也是用于监听状态变化并重建 Widget。它通过 Builder 回调来构建依赖状态的 Widget。与 watch 不同的是,Consumer 是一个 Widget,它允许你更灵活地控制依赖状态的 Widget 在 Widget 树中的位置和结构。

使用场景 :当你希望在 Widget 树的特定位置,基于状态构建一个 Widget 时,Consumer 非常有用。例如,你可能只想在页面的某个特定区域根据状态更新 UI,而不是整个 Widget 因为状态变化而重建。

代码示例

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

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Provider Consumer示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Consumer<Counter>(
                builder: (context, counter, child) {
                  return Text('计数: ${counter.count}');
                },
              ),
              ElevatedButton(
                onPressed: () {
                  context.read<Counter>().increment();
                },
                child: Text('增加计数'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在上述代码中,Consumer<Counter> 包裹的 Text 组件会根据 Counter 状态的变化而重建,通过 builder 回调构建依赖 Counter 状态的 Widget。

二、ChangeNotifierProvider

用于管理实现了 ChangeNotifier 接口的状态对象。

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

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ChangeNotifierProvider示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Consumer<Counter>(
                builder: (context, counter, child) {
                  return Text('计数: ${counter.count}');
                },
              ),
              ElevatedButton(
                onPressed: () {
                  context.read<Counter>().increment();
                },
                child: Text('增加计数'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在这个例子中,Counter 类继承自 ChangeNotifierChangeNotifierProvider 提供了 Counter 实例,Consumer 监听 Counter 的变化,当调用 increment 方法并触发 notifyListeners() 时,Consumer 包裹的 Text 会更新。

三、ValueNotifierProvider

用于管理实现了 ValueNotifier 的状态对象。

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

void main() {
  runApp(
    ValueNotifierProvider(
      create: (context) => ValueNotifier<int>(0),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ValueNotifierProvider示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Consumer<ValueNotifier<int>>(
                builder: (context, valueNotifier, child) {
                  return Text('值: ${valueNotifier.value}');
                },
              ),
              ElevatedButton(
                onPressed: () {
                  context.read<ValueNotifier<int>>().value++;
                },
                child: Text('增加值'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

这里 ValueNotifierProvider 提供了一个 ValueNotifier<int> 实例,Consumer 监听其值的变化,通过修改 value 触发更新。

四:示例场景:监听 model 中特定属性并实现局部刷新

假设 model 中有三个属性 abc,只监听其中一个属性,比如 a。可以通过自定义 ChangeNotifier 类,并在更新 a 时只通知关心 aConsumer

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

class MyModel with ChangeNotifier {
  int a = 0;
  int b = 0;
  int c = 0;

  void updateA(int newA) {
    a = newA;
    notifyListeners();
  }

  void updateB(int newB) {
    b = newB;
    // 不通知,因为我们不关心b的变化对某些Consumer的影响
  }

  void updateC(int newC) {
    c = newC;
    // 不通知,因为我们不关心c的变化对某些Consumer的影响
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('局部刷新示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Consumer<MyModel>(
                builder: (context, model, child) {
                  return Text('属性a的值: ${model.a}');
                },
              ),
              ElevatedButton(
                onPressed: () {
                  context.read<MyModel>().updateA(context.read<MyModel>().a + 1);
                },
                child: Text('增加属性a的值'),
              ),
              // 这里省略对b和c的操作示例,因为不关心它们对这个Consumer的影响
            ],
          ),
        ),
      ),
    );
  }
}

在上述代码中,MyModel 类继承自 ChangeNotifierupdateA 方法在更新 a 属性后调用 notifyListeners(),而 updateBupdateC 方法不调用。这样,只有监听 a 属性变化的 Consumer 会在 a 变化时进行局部刷新。如果要更细粒度地控制通知,可以使用 Listenable 或更复杂的状态管理模式来实现只通知特定的 Consumer。例如,使用 ValueNotifier 分别管理不同的属性,这样每个属性的变化只会通知依赖它的 Consumer

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

class MyModel {
  final ValueNotifier<int> a = ValueNotifier<int>(0);
  final ValueNotifier<int> b = ValueNotifier<int>(0);
  final ValueNotifier<int> c = ValueNotifier<int>(0);
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => MyModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('局部刷新示例'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Consumer<MyModel>(
                builder: (context, model, child) {
                  return ValueListenableBuilder<int>(
                    valueListenable: model.a,
                    builder: (context, aValue, child) {
                      return Text('属性a的值: $aValue');
                    },
                  );
                },
              ),
              ElevatedButton(
                onPressed: () {
                  context.read<MyModel>().a.value++;
                },
                child: Text('增加属性a的值'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

这里使用 ValueNotifier 分别管理 abc 属性,ValueListenableBuilder 只监听特定的 ValueNotifier,从而实现更精确的局部刷新。

相关推荐
勤劳打代码13 小时前
独辟蹊径 —— NSIS 自定义 EXE 应用名称
windows·flutter
阿笑带你学前端13 小时前
当手机遇上电视:Flutter实现局域网遥控输入的奇妙之旅
前端·flutter
早起的年轻人13 小时前
Flutter 3.35.2 以上版本中 数字转字符串的方法指南
前端·flutter
tangweiguo030519871 天前
Flutter代码生成:告别重复劳动,效率飙升
flutter
AGG_Chan1 天前
flutter专栏--深入剖析你的第一个flutter应用
前端·flutter
hqiangtai1 天前
面试复习题-Flutter
flutter·面试·职场和发展
獨孤殤2 天前
Flutter + Web:深度解析双向通信的混合应用开发实践
前端·flutter·vue