provider 4.0.2

  • Readme
  • Changelog
  • Example
  • Installing
  • 100

Build Status pub package codecov Gitter

依赖项注入(DI)和状态管理之间的混合,使用用于小部件的小部件构建.

它有目的地使用小部件进行DI /状态管理,而不是像Stream这样的仅使用飞镖的类. 原因是,小部件非常简单,但功能强大且可扩展.

通过使用小部件进行状态管理, provider可以保证:

  • 通过强制的单向数据流实现可维护性
  • 可测试性/可组合性,因为始终可以模拟/覆盖值
  • 健壮性,因为很难忘记处理模型/小部件的更新方案

要了解有关provider更多信息,请参阅文档 .

Migration from v3.x.0 to v4.0.0 #

  • 提供程序的参数builderinitialBuilder被删除.

    • initialBuilder应该由create代替.
    • builder的"代理"供应商应代之以update
    • 古典提供者的builder者应该被create代替.
  • 新的create / update回调是延迟加载的,这意味着它们是在第一次读取值时调用的,而不是在第一次创建提供程序时调用的.

    如果这是不希望的,则可以通过将lazy: false传递给您选择的提供程序来禁用延迟加载:

    FutureProvider(
      create: (_) async => doSomeHttpRequest(),
      lazy: false,
      child: ...
    )
    
  • ProviderNotFoundError重命名为ProviderNotFoundException .

  • SingleChildCloneableWidget接口已删除,并由一种新的控件SingleChildWidget代替.

    有关如何迁移的详细信息,请参见此问题 .

  • 现在, Selector深度比较先前值和新值(如果它们是集合).

    如果这是不希望的,则可以通过将shouldRebuild参数传递给Selector来恢复到旧的行为:

    Selector<Selected, Consumed>(
      shouldRebuild: (previous, next) => previous == next,
      builder: ...,
    )
    
  • DelegateWidget及其家族被删除. 相反,对于自定义提供程序,可以直接InheritedProvider或现有提供程序的子类.

Usage #

Exposing a value #

Exposing a new object instance #

提供者不仅可以公开值,还可以创建/监听/处置它.

要公开新创建的对象,请使用提供程序的默认构造函数. 不要使用.value ,如果你想创建一个对象的构造函数,或者你可另外具有不良副作用.

请参阅此stackoverflow答案该答案进一步详细说明了为什么不希望使用.value构造函数来创建值.

  • 不要createcreate一个新对象.
Provider(
  create: (_) => new MyModel(),
  child: ...
)
  • 不要使用Provider.value创建对象.
ChangeNotifierProvider.value(
  value: new MyModel(),
  child: ...
)
  • 不要使用随时间变化的变量来创建对象.

    在这种情况下,值更改时将永远不会更新您的对象.

int count;

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

如果要将随时间变化的变量传递给对象,请考虑使用ProxyProvider

int count;

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

Reusing an existing object instance: #

如果已经有对象实例并想要公开它,则应使用提供程序的.value构造函数.

否则,可能会在对象仍在使用时调用其dispose方法.

  • DO使用ChangeNotifierProvider.value提供现有ChangeNotifier .
MyChangeNotifier variable;

ChangeNotifierProvider.value(
  value: variable,
  child: ...
)
  • 不要使用默认构造函数重用现有的ChangeNotifier
MyChangeNotifier variable;

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

Reading a value #

读取值的最简单方法是使用静态方法Provider.of<T>(BuildContext context) .

从与传递的BuildContext关联的窗口小部件开始,该方法将在窗口小部件树中查找,并且它将返回找到的T类型的最近变量(如果未找到,则抛出该BuildContext ).

与第一例子结合暴露的值 ,这个小部件将读取暴露String和渲染的"Hello World".

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      Don't forget to pass the type of the object you want to obtain to `Provider.of`!
      Provider.of<String>(context)
    );
  }
}

另外,我们也可以使用ConsumerSelector来代替Provider.of .

这些对于性能优化或难以获得提供程序的BuildContext后代很有用.

有关更多信息,请参见FAQ使用者选择器的文档.

MultiProvider #

在大型应用程序中注入许多值时, Provider可能会迅速嵌套:

Provider<Something>(
  create: (_) => Something(),
  child: Provider<SomethingElse>(
    create: (_) => SomethingElse(),
    child: Provider<AnotherThing>(
      create: (_) => AnotherThing(),
      child: someWidget,
    ),
  ),
),

To:

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

这两个示例的行为严格相同. MultiProvider仅更改代码的外观.

ProxyProvider #

从3.0.0开始,提供了一种新的提供程序: ProxyProvider .

ProxyProvider是一个提供程序,它将来自其他提供程序的多个值组合到一个新对象中,并将结果发送到Provider .

然后,只要新提供者之一依赖更新,该新对象就会被更新.

下面的示例使用ProxyProvider来基于来自另一个提供程序的计数器来构建转换.

Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Counter()),
      ProxyProvider<Counter, Translations>(
        create: (_, counter, __) => Translations(counter.value),
      ),
    ],
    child: Foo(),
  );
}

class Translations {
  const Translations(this._value);

  final int _value;

  String get title => 'You clicked $_value times';
}

它具有多种变体,例如:

  • ProxyProvider vs ProxyProvider2 vs ProxyProvider3 ,...

    类名后面的数字是ProxyProvider依赖的其他提供程序的数量.

  • ProxyProvider vs ChangeNotifierProxyProvider vs ListenableProxyProvider ,...

    它们的工作原理都相似,但是ChangeNotifierProxyProvider不会将结果发送到Provider ,而是将其值发送到ChangeNotifierProvider .

FAQ #

I have an exception when obtaining Providers inside initState. What can I do?

发生此异常是因为您试图从永远不会被再次调用的生命周期中监听提供程序.

这意味着您要么应该使用另一个生命周期( didChangeDependencies / build ),要么明确指定您不关心更新.

因此,代替:

initState() {
  super.initState();
  print(Provider.of<Foo>(context).value);
}

你可以做:

Value value;

didChangeDependencies() {
  super.didChangeDependencies();
  final value = Provider.of<Foo>(context).value;
  if (value != this.value) {
    this.value = value;
    print(value);
  }
}

它将在更改value时打印value .

或者,您可以执行以下操作:

initState() {
  super.initState();
  print(Provider.of<Foo>(context, listen: false).value);
}

它将一次打印value ,并忽略更新.

I use ChangeNotifier and I have an exception when I update it, what happens?

之所以可能发生这种情况,是因为在构建小部件树时,您正在从其后代之一修改ChangeNotifier .

发生这种情况的典型情况是启动http请求时,将来存储在通知程序中:

initState() {
  super.initState();
  Provider.of<Foo>(context).fetchSomething();
}

这是不允许的,因为修改是立即的.

这意味着某些小部件可能突变之前构建,而其他小部件可能突变之后构建. 这可能会导致UI不一致,因此是不允许的.

相反,您应该在一个会平均影响整个树的地方执行该突变:

  • 直接在模型的提供者/构造函数的内部create

    class MyNotifier with ChangeNotifier {
      MyNotifier() {
        _fetchSomething();
      }
    
      Future<void> _fetchSomething() async {}
    }
    

    当没有"外部参数"时,这很有用.

  • 在帧末尾异步:

    initState() {
      super.initState();
      Future.microtask(() =>
        Provider.of<Foo>(context).fetchSomething(someValue);
      );
    }
    

    它不太理想,但是允许将参数传递给突变.

Do I have to use ChangeNotifier for complex states?

No.

您可以使用任何对象表示您的状态. 例如,另一种体系结构是将Provider.value()StatefulWidget结合使用.

这是使用这种架构的反例:

class Example extends StatefulWidget {
  const Example({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  ExampleState createState() => ExampleState();
}

class ExampleState extends State<Example> {
  int _count;

  void increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Provider.value(
      value: _count,
      child: Provider.value(
        value: this,
        child: widget.child,
      ),
    );
  }
}

我们可以通过以下方式读取状态:

return Text(Provider.of<int>(context).toString());

并使用以下命令修改状态:

return FloatingActionButton(
  onPressed: Provider.of<ExampleState>(context).increment,
  child: Icon(Icons.plus_one),
);

或者,您可以创建自己的提供程序.

Can I make my own Provider? #

是. provider了构成完整提供程序的所有小组件.

这包括:

  • SingleChildCloneableWidget ,使任何窗口小部件都可以与MultiProvider .
  • InheritedProvider ,执行Provider.of时获得的通用InheritedWidget .
  • DelegateWidget / BuilderDelegate / ValueDelegate帮助处理"创建对象的MyProvider()"与"可以随时间更新的MyProvider.value()"的逻辑.

这是一个使用ValueNotifier作为状态的自定义提供程序的示例: https : ValueNotifier

My widget rebuilds too often, what can I do? #

可以使用Consumer / Selector代替Provider.of .

它们的可选child参数仅允许重建小部件树的非常特定的部分:

Foo(
  child: Consumer<A>(
    builder: (_, a, child) {
      return Bar(a: a, child: child);
    },
    child: Baz(),
  ),
)

在此示例中,当A更新时,仅Bar将重建. FooBaz不会进行不必要的重建.

要更进一步,可以使用Selector忽略对小部件树没有影响的更改:

Selector<List, int>(
  selector: (_, list) => list.length,
  builder: (_, length, __) {
    return Text('$length');
  }
);

仅当列表长度更改时,此代码段才会重建. 但是,如果更新了项目,则不会不必要地进行更新.

Can I obtain two different providers using the same type? #

不会.虽然可以有多个共享同一类型的提供程序,但是小部件将只能获取其中之一:最接近的祖先.

相反,您必须显式地为两个提供程序提供不同的类型.

代替:

Provider<String>(
  create: (_) => 'England',
  child: Provider<String>(
    create: (_) => 'London',
    child: ...,
  ),
),

Prefer:

Provider<Country>(
  create: (_) => Country('England'),
  child: Provider<City>(
    create: (_) => City('London'),
    child: ...,
  ),
),

Existing providers #

provider者为不同类型的对象provider了几种不同类型的"提供者".

所有可用对象的完整列表在这里

namedescription
Provider 提供程序的最基本形式. 无论值是多少,它都会获取一个值并将其公开.
ListenableProvider 侦听对象的特定提供程序. 每当调用侦听器时,ListenableProvider都会侦听该对象并要求依赖于该对象的小部件进行重建.
ChangeNotifierProvider 用于ChangeNotifier的ListenableProvider的规范. 必要时它将自动调用ChangeNotifier.dispose .
ValueListenableProvider收听ValueListenable,仅公开ValueListenable.value .
StreamProvider收听流,并公开发出的最新值.
FutureProvider接受Future并在未来完成时更新依赖项.

4.0.2 #

  • 如果在didChangeDependencies内部调用,则修复Provider.of返回以前的值而不是新值.
  • 解决了不必要地调用update的问题.

4.0.1 #

  • 稳定发布4.0.0-hotfix + 1
  • 解决一些错字

4.0.0-hotfix.1 #

4.0.0 #

  • Selector现在默认情况下会深度比较集合,并提供了shouldRebuild来定制重建行为.
  • ProviderNotFoundError重命名为ProviderNotFoundException . 这允许在try/catch调用Provider.of而不触发警告.
  • 更新提供程序以与Flutter 1.12.1一起使用
  • 现在,使用提供程序创建和侦听对象的操作比较延迟. 这意味着对象是在第一次读取值时创建的,而不是第一次安装提供程序时创建的.
  • 现在将自动推断Provider.oflisten参数. 在小部件树外部调用Provider.of时,不再需要传递listen: false . 被4.0.0-hotfix删除. 参见https://github.com/rrousselGit/provider/issues/305
  • renamed initialBuilder & builder of *ProxyProvider to create & update
  • renamed builder of *Provider to create
  • 添加了*ProxyProvider0变体

3.2.0 #

  • 提供者不赞成使用"构建器",而赞成"创建"
  • 不建议使用代理提供程序的" initialBuilder" /" builder",而分别建议使用" create"和" update"

3.1.0 #

  • 添加了Selector ,类似于Consumer但是可以过滤不需要的更新
  • 改善了整体文件
  • 修正了错误ChangeNotifierProvider.value时没有更新家属ChangeNotifier例如改变.
  • Consumer现在可以在MultiProvider
     MultiProvider( providers: [ Provider(builder: (_) => Foo()), Consumer<Foo>( builder: (context, foo, child) => Provider.value(value: foo.bar, child: child), ) ], ); 

3.0.0 #

breaking (see the readme for migration steps): #

  • Provider现在如果与Listenable / Stream一起使用,则抛出该Listenable . 可以通过将Provider.debugCheckInvalidValueType设置为null来禁用它.
  • StreamProvider的默认构造函数现在已构建一个Stream而不是StreamController . 先前的行为已移至StreamProvider.controller .
  • 现在,所有XXProvider.value构造函数都使用value作为参数名称.
  • 添加了FutureProvider ,它接受将来并在将来完成时更新依赖项.
  • 提供程序不再可以使用const构造函数实例化.

non-breaking: #

  • Added ProxyProvider, ListenableProxyProvider, and ChangeNotifierProxyProvider. These providers allows building values that depends on other providers, without loosing reactivity or manually handling the state.
  • 添加了DelegateWidget和一些相关的类来帮助构建自定义提供程序.
  • 公开了内部通用InheritedWidget来帮助构建自定义提供程序.

2.0.1 #

  • 修复了一个错误,该错误使ListenableProvider.value / ChangeNotifierProvider.value / StreamProvider.value / ValueListenableProvider.value订阅/取消订阅各自的对象
  • 修复了ListenableProvider.value / ChangeNotifierProvider.value可能重建太频繁或跳过一些错误的错误.

2.0.0 #

  • Consumer现在采用一个可选的child参数来进行优化.
  • merged Provider and StatefulProvider
  • ValueListenableProvider添加了一个" builder"构造ValueListenableProvider
  • 规范化提供程序构造函数,以便默认构造函数为" builder",并提供一个名为构造函数的value .

1.6.1 #

  • Provider.of<T>现与崩溃ProviderNotFoundException时没有Provider<T>在使用的上下文的祖先中.

1.6.0 #

  • 新增内容: ChangeNotifierProvider ,类似于scoped_model,它仅在notifyListeners时公开ChangeNotifer子类并重建依赖项.
  • 新增: ValueListenableProvider ,一个提供程序,只要传递给ValueNotifier的值发生更改,该提供程序就会重建.

1.5.0 #

  • 新增:添加带有最多6个参数的Consumer .
  • 新增: MultiProvider ,一个提供程序,使提供程序树更易读
  • 新增: StreamProvider ,这是一个向其子孙公开Stream的当前值的Stream .

1.4.0 #

  • 通过修改后的原型重新引入了StatefulProvider . valueBuilderdidChangeDependencies的第二个参数已删除. 现在,在StatefulProvider的整个生命周期中只能调用一次valueBuilder .

1.3.0 #

  • 增加了Consumer ,当我们需要同时公开和使用一个值时很有用.

1.2.0 #

  • 补充: HookProvider ,一个从Hook创建其值的Provider .
  • 不推荐使用的StatefulProvider . 制作一个StatefulWidget或使用HookProvider .
  • 集成了窗口小部件检查器,以便" Provider窗口小部件显示当前值.

1.1.1 #

  • 添加didChangeDependencies回调以允许基于InheritedWidget更新值
  • ProviderStatefulProvider都添加updateShouldNotify方法

1.1.0 #

  • onDispose已添加到StatefulProvider
  • BuildContext现在传递给valueBuilder回调

example/lib/main.dart

// ignore_for_file: public_member_api_docs, lines_longer_than_80_chars
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

/// This is an example of a counter application using `provider` + [ChangeNotifier].
///
/// It builds a typical `+` button, with a twist: the texts using the counter
/// are built using the localization framework.
///
/// This shows how to bind our custom [ChangeNotifier] to things like [LocalizationsDelegate].

void main() => runApp(MyApp());

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: Consumer<Counter>(
        builder: (context, counter, _) {
          return MaterialApp(
            supportedLocales: const [Locale('en')],
            localizationsDelegates: [
              DefaultMaterialLocalizations.delegate,
              DefaultWidgetsLocalizations.delegate,
              _ExampleLocalizationsDelegate(counter.count),
            ],
            home: const MyHomePage(),
          );
        },
      ),
    );
  }
}

class ExampleLocalizations {
  static ExampleLocalizations of(BuildContext context) {
    return Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);
  }

  const ExampleLocalizations(this._count);

  final int _count;

  String get title => 'Tapped $_count times';
}

class _ExampleLocalizationsDelegate extends LocalizationsDelegate<ExampleLocalizations> {
  const _ExampleLocalizationsDelegate(this.count);

  final int count;

  @override
  bool isSupported(Locale locale) => locale.languageCode == 'en';

  @override
  Future<ExampleLocalizations> load(Locale locale) {
    return SynchronousFuture(ExampleLocalizations(count));
  }

  @override
  bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /// Tons of small widgets!
      ///
      /// Splitting our app in small widgets like [Title] or [CounterLabel] is
      /// useful for rebuild optimization.
      ///
      /// Since they are instanciated using `const`, they won't unnecessarily
      /// rebuild when their parent changes.
      /// But they can still have dynamic content, as they can obtain providers!
      ///
      /// This means only the widgets that depends on a provider to rebuild when they change.
      /// Alternatively, we could use [Consumer] or [Selector] to acheive the
      /// same result.
      appBar: AppBar(title: const Title()),
      body: const Center(child: CounterLabel()),
      floatingActionButton: const IncrementCounterButton(),
    );
  }
}

class IncrementCounterButton extends StatelessWidget {
  const IncrementCounterButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: () {
        Provider.of<Counter>(context, listen: false).increment();
      },
      tooltip: 'Increment',
      child: const Icon(Icons.add),
    );
  }
}

class CounterLabel extends StatelessWidget {
  const CounterLabel({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '${counter.count}',
          style: Theme.of(context).textTheme.display1,
        ),
      ],
    );
  }
}

class Title extends StatelessWidget {
  const Title({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(ExampleLocalizations.of(context).title);
  }
}

Use this package as a library

1. Depend on it

将此添加到包的pubspec.yaml文件中:


dependencies:
  provider: ^4.0.2

2. Install it

您可以从命令行安装软件包:

使用Flutter:


$ flutter pub get

另外,您的编辑器可能支持flutter pub get . 查看您的编辑器文档以了解更多信息.

3. Import it

现在,在Dart代码中,您可以使用:


import 'package:provider/provider.dart';
  
Popularity:
描述该程序包相对于其他程序包的受欢迎程度. [更多]
100
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
以上的加权分数. [更多]
100
了解有关得分的更多信息.

我们在2020年1月21日对该程序包进行了分析,并在下面提供了得分,详细信息和建议. using: 分析已完成,状态使用以下命令 :

  • 飞镖:2.7.0
  • 高达:0.13.4
  • Flutter:1.12.13 + hotfix.5

Health suggestions

Format lib/src/change_notifier_provider.dart.

运行flutter format以格式化lib/src/change_notifier_provider.dart .

Format lib/src/consumer.dart.

运行flutter format以格式化lib/src/consumer.dart .

Format lib/src/deferred_inherited_provider.dart.

运行flutter format以格式化lib/src/deferred_inherited_provider.dart .

修复具有分析或格式问题的其他6个文件.

以下文件中的其他问题:

  • lib/src/inherited_provider.dart (运行flutter format以格式化lib/src/inherited_provider.dart .)
  • lib/src/listenable_provider.dart (运行flutter format以格式化lib/src/listenable_provider.dart .)
  • lib/src/provider.dart (运行flutter format以格式化lib/src/provider.dart .)
  • lib/src/proxy_provider.dart (运行flutter format以格式化lib/src/proxy_provider.dart .)
  • lib/src/selector.dart (运行flutter format以格式化lib/src/selector.dart .)
  • lib/src/value_listenable_provider.dart (运行flutter format以格式化lib/src/value_listenable_provider.dart .)

Dependencies

Package Constraint Resolved Available
直接依赖
Dart SDK > = 2.2.2 <3.0.0
flutter 0.0.0
nested > = 0.0.4 <2.0.0 0.0.4
传递依存关系
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
开发依赖
flutter_test
mockito ^4.0.0

by  ICOPY.SITE