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,从而实现更精确的局部刷新。

相关推荐
恋猫de小郭7 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
明君8799712 小时前
Flutter 如何给图片添加多行文字水印
前端·flutter
四眼肥鱼19 小时前
flutter 利用flutter_libserialport 实现SQ800 串口通信
前端·flutter
火柴就是我1 天前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫1 天前
flutter接入三方库运行报错:Error running pod install
前端·flutter
shankss2 天前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
忆江南2 天前
iOS 深度解析
flutter·ios
明君879973 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter
恋猫de小郭3 天前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
MakeZero3 天前
Flutter那些事-交互式组件
flutter