Becuase flutter now support original flutter
package for web app, you can use original provider
.
Add the following codes to the pubspec.yaml
and then run flutter pub get
or flutter pub upgrade
.
dependencies:
# ... othre dependencies
provider_web:
git:
url: https://github.com/dart-for-apps/provider_web
path: packages/provider/
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.
- Providers can no longer be instantiated with
const
. Provider
now throws if used with aListenable
/Stream
. Consider usingListenableProvider
/StreamProvider
instead. Alternatively, this exception can be disabled by settingProvider.debugCheckInvalidValueType
tonull
like so:
void main() {
Provider.debugCheckInvalidValueType = null;
runApp(MyApp());
}
- All
XXProvider.value
constructors now usevalue
as parameter name.
Before:
ChangeNotifierProvider.value(notifier: myNotifier),
After:
ChangeNotifierProvider.value(value: myNotifier),
StreamProvider
's default constructor now builds aStream
instead of aStreamController
. The previous behavior has been moved to the named constructorStreamProvider.controller
.
Before:
StreamProvider(builder: (_) => StreamController<int>()),
After:
StreamProvider.controller(builder: (_) => StreamController<int>()),
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(),
)
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 the Consumer
widget.
This can be useful for performance optimizations or when it is difficult to obtain a BuildContext
descendant of the provider.
Provider<String>.value(
value: 'Hello World',
child: Consumer<String>(
builder: (context, value, child) => Text(value),
),
);
The widget Consumer
can also be used inside MultiProvider
. To do so,
it must return the child
passed to builder
in the widget tree it creates.
MultiProvider(
providers: [
Provider(builder: (_) => Foo()),
Consumer<Foo>(
builder: (context, foo, child) =>
Provider.value(value: foo.bar, child: child),
)
],
);
Note that you can freely use multiple providers with different types together:
Provider<int>.value(
value: 42,
child: Provider<String>.value(
value: 'Hello World',
child: // ...
)
)
And obtain their value independently:
var value = Provider.of<int>(context);
var value2 = Provider.of<String>(context);
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.
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
vsProxyProvider2
vsProxyProvider3
, ...That digit after the class name is the number of other providers that
ProxyProvider
depends on. -
ProxyProvider
vsChangeNotifierProxyProvider
vsListenableProxyProvider
, ...They all work similarly, but instead of sending the result into a
Provider
, aChangeNotifierProxyProvider
will send its value to aChangeNotifierProvider
.
provider
exposes a few different kinds of "provider" for different types of objects.
The complete list of all the objects availables is here
name | description |
---|---|
Provider | The most basic form of provider. It takes a value and exposes it, whatever the value is. |
ListenableProvider | A 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. |
ChangeNotifierProvider | A specification of ListenableProvider for ChangeNotifier. It will automatically call ChangeNotifier.dispose when needed. |
ValueListenableProvider | Listen to a ValueListenable and only expose ValueListenable.value . |
StreamProvider | Listen to a Stream and expose the latest value emitted. |
FutureProvider | Takes a Future and updates dependents when the future completes. |