Flutter状态共享与管理插件provider的使用

发布于 1周前 作者 yibo5220 最后一次编辑是 5天前 来自 Flutter

Flutter状态共享与管理插件provider的使用

简介

provider 是一个围绕 InheritedWidget 的包装器,旨在使其更易于使用和更具可重用性。通过使用 provider 而不是手动编写 InheritedWidget,你可以获得以下优势:

  • 简化资源分配/释放
  • 懒加载
  • 减少样板代码
  • 开发者工具友好:使用 Provider 后,应用程序的状态将在 Flutter 开发者工具中可见
  • 通用的消费方式:如 Provider.ofConsumerSelector
  • 提高复杂监听机制的可扩展性(例如 ChangeNotifier

更多关于 provider 的信息,请参阅其 官方文档

使用方法

暴露值

创建新对象实例

要暴露一个新创建的对象,使用 Provider 的默认构造函数。不要使用 .value 构造函数来创建对象,否则可能会导致不必要的副作用。

正确做法:

Provider(
  create: (_) => MyModel(),
  child: ...
)

错误做法:

ChangeNotifierProvider.value(
  value: MyModel(),
  child: ...
)

如果需要传递可能随时间变化的变量,请考虑使用 ProxyProvider

int count;

ProxyProvider0(
  update: (_, __) => MyModel(count),
  child: ...
)

重用现有对象实例

如果你已经有一个对象实例并希望暴露它,最好使用 Provider.value 构造函数。

正确做法:

MyChangeNotifier variable;

ChangeNotifierProvider.value(
  value: variable,
  child: ...
)

错误做法:

MyChangeNotifier variable;

ChangeNotifierProvider(
  create: (_) => variable,
  child: ...
)

读取值

最简单的方法是使用 BuildContext 上的扩展方法:

  • context.watch<T>():使小部件监听 T 的变化
  • context.read<T>():返回 T 而不监听它
  • context.select<T, R>(R cb(T value)):仅监听 T 的一部分

也可以使用静态方法 Provider.of<T>(context),当 listen 参数设置为 false 时,行为类似于 read

注意: context.read<T>() 不会使小部件在值变化时重建,并且不能在 StatelessWidget.buildState.build 中调用。但可以在这些方法之外自由调用。

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      context.watch<String>(),
    );
  }
}

还可以使用 ConsumerSelector 进行性能优化或难以获取 BuildContext 的情况。

可选依赖 Provider

有时我们希望支持 Provider 不存在的情况。例如,对于可在不同位置使用的可重用小部件,包括在 Provider 外部使用。

在这种情况下,当调用 context.watchcontext.read 时,将泛型类型设置为可空。

context.watch<Model?>()

如果没有找到匹配的 Provider,将返回 null 而不是抛出异常。

MultiProvider

当注入多个值时,MultiProvider 可以简化代码结构。

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  child: someWidget,
)

ProxyProvider

ProxyProvider 将多个其他 Provider 的值组合成一个新对象,并将其发送给 Provider

ProxyProvider<Counter, Translations>(
  update: (_, counter, __) => Translations(counter.value),
)

FAQ

我可以检查对象的内容吗?

可以使用 Flutter DevTools 查看对象内容。如果显示 “Instance of MyClass”,可以通过实现 DiagnosticableTreeMixin 或覆盖 toString 方法来改进显示。

initState 中获取 Provider 时出现异常怎么办?

应该在 build 方法中获取 Provider,或者明确指定不关心更新。

如何处理热重载?

让提供的对象实现 ReassembleHandler 接口。

使用 ChangeNotifier 更新时出现异常怎么办?

确保不在构建树时修改 ChangeNotifier,可以在 create 回调中或异步执行更新操作。

是否必须使用 ChangeNotifier 处理复杂状态?

不一定,可以使用任何对象表示状态,例如结合 Provider.value()StatefulWidget

可以创建自定义 Provider 吗?

可以,provider 暴露了所有用于创建完整 Provider 的小组件。

小部件重建过于频繁怎么办?

使用 context.select 仅监听特定属性,或使用 ConsumerSelector 优化重建范围。

可以使用同一类型获取两个不同的 Provider 吗?

不可以,应显式地为每个 Provider 分配不同的类型。

可以消费接口并提供实现吗?

可以,编译器需要类型提示以指示消费接口并在 create 中提供实现。

现有 Provider

provider 提供了几种不同类型的 Provider:

名称 描述
Provider 最基本的 Provider,暴露任意值
ListenableProvider 专门用于 Listenable 对象
ChangeNotifierProvider 专用于 ChangeNotifier 的 ListenableProvider
ValueListenableProvider 监听 ValueListenable 并仅暴露其值
StreamProvider 监听 Stream 并暴露最新值
FutureProvider 监听 Future 并在完成时更新依赖

解决 StackOverflowError

如果应用中有大量 Provider(150+),可能会导致某些设备抛出 StackOverflowError。解决方案包括逐步挂载 Provider 或放弃使用 MultiProvider

赞助商


更多关于Flutter状态共享与管理插件provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态共享与管理插件provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,状态共享与管理是一个常见的需求,Provider插件为此提供了一个简单而强大的解决方案。Provider允许你在widget树中共享数据,而无需通过复杂的构造函数参数逐级传递。下面是一个关于如何使用Provider的基本示例,包括Provider的几种常见用法:ProviderChangeNotifierProviderValueListenableProvider

1. 安装Provider

首先,确保你已经在pubspec.yaml文件中添加了Provider依赖:

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.0  # 请根据需要检查最新版本号

然后运行flutter pub get来安装依赖。

2. 使用Provider

2.1 创建共享数据类

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();  // 通知监听者数据已更改
  }
}

2.2 使用ChangeNotifierProvider

在MaterialApp的顶层使用ChangeNotifierProvider来提供Counter实例:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider Demo')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Consumer<Counter>(
                builder: (_, counter, __) {
                  return Text(
                    'You have pushed the button this many times:',
                    style: Theme.of(context).textTheme.headline4,
                  );
                },
                child: Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ),
              SizedBox(height: 20),
              FloatingActionButton(
                onPressed: () {
                  context.read<Counter>().increment();
                },
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

3. 使用ValueListenableProvider

如果你不需要通知监听者执行任意代码(只是监听值的变化),可以使用ValueListenableProvider。下面是一个简单的例子:

class ValueCounter with ValueNotifier<int> {
  ValueCounter() : super(0);

  void increment() {
    value++;
  }
}

// 在MaterialApp的顶层使用ValueListenableProvider
void main() {
  runApp(
    ValueListenableProvider(
      create: (_) => ValueCounter(),
      child: MyValueListenableApp(),
    ),
  );
}

class MyValueListenableApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('ValueListenableProvider Demo')),
        body: Center(
          child: ValueListenableBuilder<int>(
            valueListenable: context.watch<ValueCounter>(),
            builder: (_, value, __) {
              return Text('Count: $value');
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            context.read<ValueCounter>().increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

以上代码展示了如何使用Provider插件在Flutter中实现状态共享与管理。通过ChangeNotifierProviderValueListenableProvider,你可以轻松地在widget树中共享和监听数据变化,而无需手动逐级传递状态。

回到顶部