One, foreword
Provider is one of the status management methods recommended by Google. You are advised to check the Github address of Provider to learn the basic usage.
Most of the articles on the web that introduce providers are about ChangeNotifierProvider, and after reading it you really know what it does and how to use it.
However, there are other providers for us to use, so what are the differences and connections between them? There is no detailed Demo of their use in official documentation, so this article will summarize their usage and differences.
Providers are classified as follows:
Provider
ListenableProvider
ChangeNotifierProvider
ValueListenableProvider
StreamProvider
FutureProvider
Second, the Provider
2.1 Constructors
The constructor of the Provider is as follows:
Provider({
Key key,
@required ValueBuilder<T> builder,
Disposer<T> dispose,
Widget child,
})
Copy the code
builder
:T Function(BuildContext context)
Returns the data to be sharedModel
.dispose
:void Function(BuildContext context, T value)
To release resources in a callback.
2.2 Advantages & Disadvantages
Advantages of Providers:
- Data sharing: Yes
Provider.of<T>(context)
Method, can be in toProvider
Is obtained from the subtree of the root nodeT
The object. - through
dispose
Parameter to release resources.
However, providers have an obvious drawback:
- Its listeners cannot be notified when shared data changes.
2.3 Counter Examples
Let’s use a classic counter Demo to demonstrate the use of the Provider. For comparison purposes, we’ll use the same example when we introduce other providers.
According to the two features of Provider, we can use it to obtain sink in BLoc and finally release resources.
- First let’s define a simple one
Bloc
Model.
import 'dart:async';
class ProviderBloc {
int _count = 0;
var _countController = StreamController<int>.broadcast();
Stream<int> get stream => _countController.stream;
int getcount => _count; increment() { _countController.sink.add(++_count); } dispose() { _countController.close(); }}Copy the code
- Next, write the main interface.
- through
Provider.of<ProviderBloc>(context)
We can get it anywhereProviderBloc
Object, getsink
To change the data. - use
StreamBuilder
Listening to theStream
In the data changes, refresh the interface. - In the original
Bloc
On the model, the top levelProvider
providesdispose
Callback for resource release.
- through
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_bloc.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<ProviderBloc>(
builder: (context) => ProviderBloc(),
dispose: (context, bloc) => bloc.dispose(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')), body: CounterLabel(), floatingActionButton: CounterButton(), ), ), ); }}class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<ProviderBloc>(context).increment,
child: constIcon(Icons.add), ); }}class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder<int>(
builder: (context, snapshot) {
return Text('you have push ${snapshot.data} times');
},
initialData: 0, stream: Provider.of<ProviderBloc>(context).stream, ), ); }}Copy the code
Third, ChangeNotifierProvider
ChangeNotifierProvider should be the most common, most of the introduction of Provider articles are based on it as an example, compared with the Provider, its biggest advantage is to solve the problem of not listening after data changes.
3.1 Constructors
The ChangeNotifierProvider constructor is as follows:
ChangeNotifierProvider({
Key key,
@required ValueBuilder<T> builder,
Widget child,
})
Copy the code
builder
:T Function(BuildContext context)
Returns the data to be sharedModel
.
3.2 ChangeNotifier
When using ChangeNotifierProvider, it requires that the data Model returned from Builder be a subclass of ChangeNotifier.
- After changing the data, call
notifyListener()
Methods. - By rewriting it
dispose
Method, can complete the sumProvider
Same resource release work.
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
@override
void dispose() {
super.dispose(); }}Copy the code
3.3 the Provider of (context) & Consumer
In Provider articles, both provider.of
(context) and Consumer are compared, and Consumer is generally recommended because it reconstructs the listener’s Widget more narrowly after data changes.
The use of providers in a project can be divided into two roles, the trigger of data changes and the listener:
- Trigger: If you just need to get the data
model
, does not need to listen for changes (such as button clicking), recommendedProvider.of<Counter>(context, listen: false)
To get the datamodel
. - Listener: Recommended
Consumer
.
3.4 example
3.4.1 Defining the data model
- First, define the data model:
- After changing the data, call
notifyListeners
Methods. - If resources need to be freed, they need to be overridden
dispose
Methods.
- After changing the data, call
import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
@override
void dispose() {
super.dispose(); }}Copy the code
3.4.2 master file
import 'counter_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Counter>(
builder: (context) => Counter(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')), body: CounterLabel(), floatingActionButton: CounterButton(), ), ) ); }}class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context, listen : false).increment,
child: constIcon(Icons.add), ); }}class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<Counter>(
builder: (BuildContext context, Counter counter, Widget child) {
return Text('you have push ${counter.count} times'); })); }}Copy the code
Four, ListenableProvider
4.1 Constructors
The ListenableProvider constructor is:
ListenableProvider({
Key key,
@required ValueBuilder<T> builder,
Disposer<T> dispose,
Widget child,
})
Copy the code
builder
:T Function(BuildContext context)
Returns the data to be sharedModel
.dispose
:void Function(BuildContext context, T value)
To release resources in a callback.
4.2 Comparison with ChangeNotifierProvider
ChangeNotifierProvider ChangeNotifierProvider
class ChangeNotifierProvider<T extends ChangeNotifier>
extends ListenableProvider<T> implements SingleChildCloneableWidget {
static void_disposer(BuildContext context, ChangeNotifier notifier) => notifier? .dispose(); ChangeNotifierProvider({ Key key,@required ValueBuilder<T> builder,
Widget child,
}) : super(key: key, builder: builder, dispose: _disposer, child: child);
}
Copy the code
It can be seen from the source code, ListenableProvider and ChangeNotifierProvider is actually the father and son of the relationship, ChangeNotifierProvider on its basis:
- Limited data
model
The upper limit, the requirement must beChangeNotifier
The subclass. - Through rewriting
ChangeNotifier.dispose()
To complete the resource release without passing indispose
Parameters toChangeNotifierProvider
.
4.3 example
When using ListenableProvider, if we do not define the data model as a subclass of ChangeNotifier, then we need to manage listeners ourselves. For convenience, we will continue to use ChangeNotifier. The use of the ChangeNotifierProvider is the same elsewhere.
import 'counter_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListenableProvider<Counter>(
builder: (context) => Counter(),
dispose: (context, counter) => counter.dispose(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')), body: CounterLabel(), floatingActionButton: CounterButton(), ), ) ); }}class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context, listen: false).increment,
child: constIcon(Icons.add), ); }}class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<Counter>(
builder: (BuildContext context, Counter counter, Widget child) {
return Text('you have push ${counter.count} times'); })); }}Copy the code
Fifth, ValueListenableProvider
5.1 Constructors
ValueListenableProvider({
Key key,
@required ValueBuilder<ValueNotifier<T>> builder,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
Copy the code
builder
:T Function(BuildContext context)
Returns the data to be sharedModel
.dispose
:void Function(BuildContext context, T value)
To release resources in a callback.
5.2 ValueNotifier
ValueListenableProvider requires that the object returned by Builder must be a subclass of ValueNotifier
, where T is the data type to be shared.
ValueNotifier is defined as follows:
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
ValueNotifier(this._value);
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
Copy the code
ValueNotifier is a subclass of ChangeNotifier. When changing _value, notifyListeners are automatically called.
5.3 example
5.3.1 Defining subclasses of ValueNotifier
import 'package:flutter/foundation.dart';
class CounterModel {
int count;
CounterNotifier wrapper;
CounterModel(this.count);
}
class CounterNotifier extends ValueNotifier<CounterModel> {
CounterNotifier(CounterModel value) : super(value) {
value.wrapper = this;
}
@override
void dispose() {
super.dispose(); }}Copy the code
5.3.2 master file
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_notifier.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableProvider<CounterModel>(
builder: (context) => CounterNotifier(CounterModel(0)),
updateShouldNotify: (model1, model2) {
print('updateShouldNotify');
returnmodel1.count ! = model2.count; }, child: MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Provider Demo')), body: _CounterLabel(), floatingActionButton: _CounterButton(), ), ) ); }}class _CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
CounterModel oldModel = Provider.of<CounterModel>(context, listen: false);
CounterModel newModel = CounterModel(oldModel.count + 1);
newModel.notifier = oldModel.notifier;
oldModel.notifier.value = newModel;
return;
},
child: constIcon(Icons.add), ); }}class _CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<CounterModel>(
builder: (BuildContext context, CounterModel model, Widget child) {
return Text('you have push ${model.count} times'); })); }}Copy the code
5.4 question
Notice how notifyListeners() are triggered by changing _value when using ValueNotifier
, whereas provider. of
(context, listen: _value is false) to get the object, so you also need to save ValueNotifier within it < T > references (or defined ValueNotifier into a singleton pattern), to set up a triggered effect, feel very strange to use, the use of don’t know if I have a question, There are no examples online.
Six, StreamProvider
StreamProvider is used in conjunction with Bloc.
6.1 Constructors
6.1.1 Using the Stream construct
StreamProvider({
Key key,
@required ValueBuilder<Stream<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
Copy the code
builder
Returns theBloc
In theStream
.initialData
: Initial data.catchError
: Callback when an error occurs.
6.1.2 Using the StreamController construct
StreamProvider.controller({
Key key,
@required ValueBuilder<StreamController<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
Copy the code
builder
Returns theBloc
In theStreamController
.
6.2 example
6.2.1 Defining the Singleton Mode
import 'dart:async';
class ProviderBloc {
static ProviderBloc _instance;
ProviderBloc._internal() {
print("_internal");
}
static ProviderBloc _getInstance() {
if (_instance == null) {
_instance = ProviderBloc._internal();
}
return _instance;
}
factory ProviderBloc() => _getInstance();
static ProviderBloc get instance => _getInstance();
int _count = 0;
var _countController = StreamController<int>.broadcast();
Stream<int> get stream => _countController.stream;
int getcount => _count; increment() { _countController.sink.add(++_count); } dispose() { _countController.close(); }}Copy the code
6.2.2 master file
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_bloc.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamProvider<int> (
builder: (context) {
return ProviderBloc().stream;
},
catchError: (BuildContext context, Object error) {},
initialData: 0,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')), body: CounterLabel(), floatingActionButton: CounterButton(), ), ) ); }}class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: ProviderBloc().increment,
child: constIcon(Icons.add), ); }}class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<int>(
builder: (BuildContext context, int value, Widget child) {
return Text('you have push $value times'); })); }}Copy the code
Seven, FutureProvider
7.1 Constructors
FutureProvider({
Key key,
@required ValueBuilder<Future<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
Copy the code
builder
: Returns aFuture<T>
Object, which is emitted when the result is returned by an asynchronous taskConsumer
The reconstruction.initialData
: Initial data.catchError
: An error callback occurred.
7.2 And FutureBuilder differences
Unlike the Provider scenario we discussed earlier, FutureProvider is similar to FutureBuilder in that it loads a component before data is returned, waits for the data to return a value, and then redraws to return another component.
The differences between it and FutureBuilder are:
FutureBuilder
The data request and presentation are all in one component, whileFutureProvider
Is the requester of the data,Consumer
It’s the showman.FutureBuilder
The requester and presenter are in a one-to-one relationship, whileFutureProvider
It could be a one-to-many relationship.
7.3 example
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureProvider<int>(
builder: (context) => _request(),
initialData: 0,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
),
)
);
}
Future<int> _request() async {
return await Future<int>.delayed(Duration(milliseconds: 3000)).then((int value) {
return 300; }); }}class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(children: <Widget>[
Consumer<int>(
builder: (BuildContext context, int count, Widget child) {
return Text('Observer1=$count');
}),
Consumer<int>(
builder: (BuildContext context, int count, Widget child) {
return Text('Observer2=$count'); }),])); }}Copy the code
Seven, summary
Compare the usage modes of the preceding providers: ChangeNotifierProvider and StreamProvider are more suitable for our daily use scenarios.
And the other three:
Provider
: Cannot monitor data changes, directly out.ListenableProvider
:ChangeNotifierProvider
The original version of.ValueListenableProvider
:ChangeNotifierProvider
“, but it feels even more troublesome to use.