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

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

简介

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

  • 简化资源分配/释放
  • 懒加载
  • 减少样板代码
  • 开发者工具友好:使用 Provider 后,应用程序的状态将在 Flutter 开发者工具中可见
  • 通用的消费方式:如 Provider.of、Consumer 和 Selector
  • 提高复杂监听机制的可扩展性(例如 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.build 或 State.build 中调用。但可以在这些方法之外自由调用。

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

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

可选依赖 Provider

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

在这种情况下,当调用 context.watch 或 context.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 仅监听特定属性,或使用 Consumer 和 Selector 优化重建范围。

可以使用同一类型获取两个不同的 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的几种常见用法:Provider、ChangeNotifierProvider和ValueListenableProvider。

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中实现状态共享与管理。通过ChangeNotifierProvider和ValueListenableProvider,你可以轻松地在widget树中共享和监听数据变化,而无需手动逐级传递状态。

回到顶部