provider 3.1.0

Build Status pub package codecov Gitter

A mixture between dependency injection (DI) and state management, built with widgets for widgets.

It purposefully uses widgets for DI/state management instead of dart-only classes like Stream. The reason is, widgets are very simple yet robust and scalable.

By using widgets for state management, provider can guarantee:

  • maintainability, through a forced uni-directional data-flow
  • testability/composability, since it is always possible to mock/override a value
  • robustness, as it is harder to forget to handle the update scenario of a model/widget

To read more about provider, see the documentation.

Migration from v2.0.0 to v3.0.0 #

  • Providers can no longer be instantiated with const.
  • Provider now throws if used with a Listenable/Stream. Consider using ListenableProvider/StreamProvider instead. Alternatively, this exception can be disabled by setting Provider.debugCheckInvalidValueType to null like so:
void main() {
  Provider.debugCheckInvalidValueType = null;

  runApp(MyApp());
}
  • All XXProvider.value constructors now use value as parameter name.

Before:

ChangeNotifierProvider.value(notifier: myNotifier),

After:

ChangeNotifierProvider.value(value: myNotifier),
  • StreamProvider's default constructor now builds a Stream instead of a StreamController. The previous behavior has been moved to the named constructor StreamProvider.controller.

Before:

StreamProvider(builder: (_) => StreamController<int>()),

After:

StreamProvider.controller(builder: (_) => StreamController<int>()),

Usage #

Exposing a value #

To expose a variable using provider, wrap any widget into one of the provider widgets from this package and pass it your variable. Then, all descendants of the newly added provider widget can access this variable.

A simple example would be to wrap the entire application into a Provider widget and pass it our variable:

Provider<String>.value(
  value: 'Hello World',
  child: MaterialApp(
    home: Home(),
  )
)

Alternatively, for complex objects, most providers expose a constructor that takes a function to create the value. The provider will call that function only once, when inserting the widget in the tree, and expose the result. This is perfect for exposing a complex object that never changes over time without writing a StatefulWidget.

The following creates and exposes a MyComplexClass. And in the event where Provider is removed from the widget tree, the instantiated MyComplexClass will be disposed.

Provider<MyComplexClass>(
  builder: (context) => MyComplexClass(),
  dispose: (context, value) => value.dispose()
  child: SomeWidget(),
)

Reading a value #

The easiest way to read a value is by using the static method Provider.of<T>(BuildContext context).

This method will look up in the widget tree starting from the widget associated with the BuildContext passed and it will return the nearest variable of type T found (or throw if nothing is found).

Combined with the first example of exposing a value, this widget will read the exposed String and render "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)
    );
  }
}

Alternatively instead of using Provider.of, we can use Consumer and Selector.

These can be useful for performance optimizations or when it is difficult to obtain a BuildContext descendant of the provider.

See the FAQ or the documentation of Consumer and Selector for more information.

MultiProvider #

When injecting many values in big applications, Provider can rapidly become pretty nested:

Provider<Foo>.value(
  value: foo,
  child: Provider<Bar>.value(
    value: bar,
    child: Provider<Baz>.value(
      value: baz,
      child: someWidget,
    )
  )
)

In that situation, we can use MultiProvider to improve the readability:

MultiProvider(
  providers: [
    Provider<Foo>.value(value: foo),
    Provider<Bar>.value(value: bar),
    Provider<Baz>.value(value: baz),
  ],
  child: someWidget,
)

The behavior of both examples is strictly the same. MultiProvider only changes the appearance of the code.

ProxyProvider #

Since the 3.0.0, there is a new kind of provider: ProxyProvider.

ProxyProvider is a provider that combines multiple values from other providers into a new object, and sends the result to Provider.

That new object will then be updated whenever one of the providers it depends on updates.

The following example uses ProxyProvider to build translations based on a counter coming from another provider.

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

class Translations {
  const Translations(this._value);

  final int _value;

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

It comes under multiple variations, such as:

  • ProxyProvider vs ProxyProvider2 vs ProxyProvider3, ...

    That digit after the class name is the number of other providers that ProxyProvider depends on.

  • ProxyProvider vs ChangeNotifierProxyProvider vs ListenableProxyProvider, ...

    They all work similarly, but instead of sending the result into a Provider, a ChangeNotifierProxyProvider will send its value to a ChangeNotifierProvider.

FAQ #

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

Instead of Provider.of, you can use Consumer/Selector.

Their optional child argument allows to rebuild only a very specific part of the widget tree:

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

In this example, only Bar will rebuild when A updates. Foo and Baz won't unnecesseraly rebuild.

To go one step further, it is possible to use Selector to ignore changes if they don't have an impact on the widget-tree:

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

This snippet will rebuild only if the length of the list changes. But it won't unnecessarily update if an item is updated.

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

No. While you can have multiple providers sharing the same type, a widget will be able to obtain only one of them: the closest ancestor.

Instead, you must explicitly give both providers a different type.

Instead of:

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

Prefer:

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

Existing providers #

provider exposes a few different kinds of "provider" for different types of objects.

The complete list of all the objects availables is here

namedescription
ProviderThe most basic form of provider. It takes a value and exposes it, whatever the value is.
ListenableProviderA specific provider for Listenable object. ListenableProvider will listen to the object and ask widgets which depend on it to rebuild whenever the listener is called.
ChangeNotifierProviderA specification of ListenableProvider for ChangeNotifier. It will automatically call ChangeNotifier.dispose when needed.
ValueListenableProviderListen to a ValueListenable and only expose ValueListenable.value.
StreamProviderListen to a Stream and expose the latest value emitted.
FutureProviderTakes a Future and updates dependents when the future completes.

3.1.0 #

  • Added Selector, similar to Consumer but can filter unneeded updates
  • improved the overall documentation
  • fixed a bug where ChangeNotifierProvider.value didn't update dependents when the ChangeNotifier instance changed.
  • Consumer can now be used inside 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 now throws if used with a Listenable/Stream. This can be disabled by setting Provider.debugCheckInvalidValueType to null.
  • The default constructor of StreamProvider has now builds a Stream instead of StreamController. The previous behavior has been moved to StreamProvider.controller.
  • All XXProvider.value constructors now use value as parameter name.
  • Added FutureProvider, which takes a future and updates dependents when the future completes.
  • Providers can no longer be instantiated using const constructors.

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.
  • Added DelegateWidget and a few related classes to help building custom providers.
  • Exposed the internal generic InheritedWidget to help building custom providers.

2.0.1 #

  • fix a bug where ListenableProvider.value/ChangeNotifierProvider.value /StreamProvider.value/ValueListenableProvider.value subscribed/unsubscribed to their respective object too often
  • fix a bug where ListenableProvider.value/ChangeNotifierProvider.value may rebuild too often or skip some.

2.0.0 #

  • Consumer now takes an optional child argument for optimization purposes.
  • merged Provider and StatefulProvider
  • added a "builder" constructor to ValueListenableProvider
  • normalized providers constructors such that the default constructor is a "builder", and offer a value named constructor.

1.6.1 #

  • Provider.of<T> now crashes with a ProviderNotFoundException when no Provider<T> are found in the ancestors of the context used.

1.6.0 #

  • new: ChangeNotifierProvider, similar to scoped_model that exposes ChangeNotifer subclass and rebuilds dependents only when notifyListeners is called.
  • new: ValueListenableProvider, a provider that rebuilds whenever the value passed to a ValueNotifier change.

1.5.0 #

  • new: Add Consumer with up to 6 parameters.
  • new: MultiProvider, a provider that makes a tree of provider more readable
  • new: StreamProvider, a stream that exposes to its descendants the current value of a Stream.

1.4.0 #

  • Reintroduced StatefulProvider with a modified prototype. The second argument of valueBuilder and didChangeDependencies have been removed. And valueBuilder is now called only once for the whole life-cycle of StatefulProvider.

1.3.0 #

  • Added Consumer, useful when we need to both expose and consume a value simultaneously.

1.2.0 #

  • Added: HookProvider, a Provider that creates its value from a Hook.
  • Deprecated StatefulProvider. Either make a StatefulWidget or use HookProvider.
  • Integrated the widget inspector, so that Provider widget shows the current value.

1.1.1 #

  • add didChangeDependencies callback to allow updating the value based on an InheritedWidget
  • add updateShouldNotify method to both Provider and StatefulProvider

1.1.0 #

  • onDispose has been added to StatefulProvider
  • BuildContext is now passed to valueBuilder callback

example/lib/main.dart

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

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(builder: (_) => 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) =>
      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) =>
      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(
      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: () {
        // `listen: false` is specified here because otherwise that would make
        // `IncrementCounterButton` rebuild when the counter updates.
        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

Add this to your package's pubspec.yaml file:


dependencies:
  provider: ^3.1.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:provider/provider.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
100
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
100
Learn more about scoring.

We analyzed this package on Sep 17, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.0
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.2

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.7
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
mockito ^4.0.0
pedantic ^1.4.0