preface

Provider is an official recommended state management method announced at Google I/O 2019.

This document describes the features and usage of Provider through a simple project.

For an official introduction to Provider, click Provider.

The project address

Although the article is long, the code to make up. To see the code directly, click github.com/smiling1990…

Classification of the Provider

Provider

The most basic Provider is one that holds a value of a type, exposes it, and retrieves the latest value at any time. In general, the lower layer picks up the upper state, but the lower layer does not listen for changes in the upper state. As your application gets more complex, it’s convenient to use it to share state across widgets.

Note: A Provider can maintain only one copy of data of the same type.

ListenableProvider

Listen for state changes and redraw the view.

ChangeNotifierProvider

Different from ListenableProvider, Dispose is automatically called when needed.

ValueListenableProvider

The difference between ListenableProvider and ListenableProvider is that only value is supported to obtain the status.

StreamProvider

Share data as a stream.

FutureProvider

Holds the Future and notifies listeners when the Future is complete.

ProxyProvider series

When a Model is dependent on other models or more models, that is, when multiple Model states change, the state of this Model will change, so it is necessary to use the ProxyProvider series to establish the dependency relationship between multiple models.

ProxyProvider series includes:

  • ProxyProvider – ProxyProvider6
  • ListenableProxyProvider – ListenableProxyProvider6
  • ChangeNotifierProxyProvider – ChangeNotifierProxyProvider6
  • xxxProxyProvider

The use of these ProxyProviders is basically the same as the use of Providers, which will be introduced in the following sections.

Which Provider to use?

  • Requirement: Multiple widgets share state. If there is no requirement, well, why not? Strange.
  • When there is no need to listen for state changes, the lower layer simply gets the upper state, using [Provider].
  • To share data and listen for data changes, use [ListenableProvider] or [ChangeNotifierProvider]. If Dispose dispose manages data independently, use ListenableProvider.
  • If two or more types of data depend on each other, use the [ProxyProvider] series.
  • The use of the most convenient, is also the lazy way, the strongest use [ChangeNotifierProvider] and [ChangeNotifierProxyProvider].
  • Technology is constantly improving, official updates are more frequent, need to pay attention to the dynamic.

Two ways to get state

The value can be obtained through provider.value (context)

It is flexible and easy to use, and if the Widget is simple, getting the state is a single line of code.

Project managers who know what they’re doing, sometimes say, “This, this, and that, it’s just one line of code,” and we, how many times do we have to go through this line of code, Init, Model, official dynamics, performance, bugs, bugs? !

Obtain it from Consumer

It is recommended to use this method to avoid redrawing the entire Widget. Partial Widget refresh redraw and entire Widget redraw, for example:

  • Widget drawing: Nami eats 10 pieces of meat from Luffy’s hand
  • Widget partial drawing: Nami needs to eat one piece of luffy’s meat
  • Widget Redraw: Nami needs to eat 10 pieces of luffy’s flesh

The project of actual combat

Project introduction

  1. Change the project’s theme color PrimaryColor

    Predicted result: Changing Model data in the child Widget causes the state of the parent Widget to change.

  1. The home page displays Setting, User Info, and User Address via ListView

  1. Change Setting, User Info, User Address data and observe the redrawing of the page

    Predicted result: Only a single Widget will be redrawn

  2. Establish a dependency between User Address and User Info, change the User Info data, and observe the page redrawing.

    Predicted result: Changing UserInfo data redraws UserInfoWidget and UserAddressWidget.

The project structure

Step 1: Add dependencies

The latest version is 3.2.0. To view the latest version, click Provider Latest Version

  • Add provider to pubspec.yaml: ^3.2.0
  • Perform Package get

Step 2: Create the Model

class ThemeModel extends BaseModel with ChangeNotifier { Color _primaryColor; ThemeModel(this._primaryColor); Color get primaryColor => _primaryColor; void update(Color primaryColor) { _primaryColor = primaryColor; notifyListeners(); }}Copy the code
class SettingModel extends BaseModel with ChangeNotifier { /// Application id String _id; /// Application name String _name; /// Version String _version; /// Version code int _versionCode; SettingModel( this._id, this._name, this._version, this._versionCode, ); String get id => _id; String get name => _name; String get version => _version; int get versionCode => _versionCode; void update({String id, String name, String version, int versionCode}) { _id = id ?? _id; _name = name ?? _name; _version = version ?? _version; _versionCode = versionCode ?? _versionCode; notifyListeners(); }}Copy the code
class UserInfoModel extends BaseModel with ChangeNotifier { /// User id String _id; /// User age int _age; /// User name String _name; /// User avatar String _avatar; /// User email String _email; /// User Personal Net String _net; UserInfoModel( this._id, this._name, this._age, this._avatar, this._email, this._net, ); String get id => _id; int get age => _age; String get name => _name; String get avatar => _avatar; String get email => _email; String get net => _net; void update({String id, String name, String avatar, int age}) { _id = id ?? _id; _age = age ?? _age; _name = name ?? _name; _avatar = avatar ?? _avatar; notifyListeners(); }}Copy the code
class UserAddressModel extends BaseModel with ChangeNotifier {
  /// User
  UserInfoModel _userInfoModel;

  /// Address id
  String _id;

  /// PostalCode
  String _postalCode;

  /// Address
  String _address;

  /// Province
  String _province;

  /// City
  String _city;

  /// District
  String _district;

  UserAddressModel(
    this._userInfoModel,
    this._id,
    this._postalCode,
    this._address,
    this._province,
    this._city,
    this._district,
  );

  UserInfoModel get userInfoModel => _userInfoModel;

  String get id => _id;

  String get postalCode => _postalCode;

  String get address => _address;

  String get province => _province;

  String get city => _city;

  String get district => _district;

  void update({
    UserInfoModel userInfoModel,
    String id,
    String postalCode,
    // String address,
    String province,
    String city,
    String district,
  }) {
    _userInfoModel = userInfoModel ?? _userInfoModel;
    _id = id ?? _id;
    _postalCode = postalCode ?? _postalCode;
    _province = province ?? _province;
    _city = city ?? _city;
    _district = district ?? _district;
    _address = district == null ? _address : $district, Shenzhen city, Guangdong Province; notifyListeners(); }}Copy the code

Step 3: Create a Store

[Provider] The most basic Provider. It holds a value of a type, exposes it, and obtains the latest value at any time. /// In normal cases, the lower layer gets the state of the upper layer, but the lower layer cannot listen for the state change of the upper layer. Using /// to share state across widgets is convenient as applications become more complex. /// 2. [ListenableProvider] listens for state changes and redraws the view. /// 3. [ChangeNotifierProvider] differs from ListenableProvider in that, when needed, Dispose. /// 4. [ValueListenableProvider] Is different from ListenableProvider in that it only supports the value mode to obtain the status. /// 5. [StreamProvider] Shares data as a stream. /// 6. [FutureProvider] holds the Future and notifies listeners when the Future is complete. [ProxyProvider] 7.1 [ProxyProvider] // 7.2 [ProxyProvider2] - [ProxyProvider6] // 7.3 [listenabproxyprovider] // 7.4 [listenabproxyprovider] - [listenabproxyprovider6] // 7.5... /// Features: Only one piece of data can be maintained for the same type. Requirement: Multiple widgets share state. If there is no requirement, well, why not? Strange. /// 1. When there is no need to listen to the state change, the lower layer only obtains the upper layer state, and uses [Provider]. Use [ListenableProvider] or [ChangeNotifierProvider] to dispose the data dispose, Use ListenableProvider. // 3. If two or more types of data depend on each other, use the [ProxyProvider] series. / / / 4. The most convenient to use, is also the lazy way, the strongest use [ChangeNotifierProvider] and [ChangeNotifierProxyProvider]. / / / 5. Technology is constantly improving, official updates are more frequent, need to pay attention to the dynamic. Class Store {/ / / to create a single Provider] / / / when using this method, need to be in the main, adding the Provider. The dart debugCheckInvalidValueType = null; static Provider createProvider<T>(T t) {returnProvider<T>( create: (BuildContext c) => t, ); } // create a single [ListenableProvider] static ListenableProvider createListenableProvider<T extends Listenable>(T T) {returnListenableProvider<T>( create: (BuildContext c) => t, ); } // create a single [ChangeNotifierProvider] // prompt: [ChangeNotifier] implements [Listenable] static ChangeNotifierProvider createChangeNotifierProvider < T extends ChangeNotifier > (T  t) {returnChangeNotifierProvider<T>( create: (BuildContext c) => t, ); } / / / to create a single [ProxyProvider] / / / when using this method, need to be in the main, adding the Provider. The dart debugCheckInvalidValueType = null; static ProxyProvider createProxyProvider<T, R>( R r, R Function(BuildContext c, T t, R r) update, ) {returnProxyProvider<T, R>( create: (BuildContext c) => r, update: update, ); } / / / to create a single [ListenableProxyProvider] static ListenableProxyProvider createListenableProxyProvider < T, R extends Listenable>( R r, R Function(BuildContext c, T t, R r) update, ) {returnListenableProxyProvider<T, R>( create: (BuildContext c) => r, update: update, ); } / / / to create a single [ChangeNotifierProxyProvider] static ChangeNotifierProxyProvider createChangeNotifierProxyProvider < T, R extends ChangeNotifier>( R r, R Function(BuildContext c, T t, R r) update, ) {returnChangeNotifierProxyProvider<T, R>( create: (BuildContext c) => r, update: update, ); } // the upper-layer Widget executes, Init /// 1. If the Application is shared, call /// 2 in RunApp. If only a few widgets share state, Use static init({Widget Child,}) {var themeModel = themeModel (Color(0xFFC12CC6),) on the common parent Widget of these widgets; var userInfoModel = UserInfoModel('smiling_0906'.'Blue Smile ing', 18.'assets/avatar_0.png'.'[email protected]'.'https://juejin.cn/user/3808364011464840',); var settingModel = SettingModel('com.smiling,flutterprovider'.'Flutter Provider'.'1.0.1', 1); var addressModel = UserAddressModel( userInfoModel,'AID1'.'518100'.Bao 'an District, Shenzhen City, Guangdong Province.'Guangdong'.'Shenzhen'.'Bao 'an District',);return MultiProvider(
      providers: [
        createListenableProvider<ThemeModel>(themeModel),
        createListenableProvider<UserInfoModel>(userInfoModel),
        createListenableProvider<SettingModel>(settingModel),
        createChangeNotifierProxyProvider<UserInfoModel, UserAddressModel>(
          addressModel,
          (BuildContext c, UserInfoModel u, UserAddressModel a) {
            returna.. update(userInfoModel: u); }, ), ], child: child, ); } // The parent Widget executes [initSomeProviders], and the child Widget gets State. Don't them Provider on the unrelated Widget State static initSomeProviders ({List < SingleChildCloneableWidget > will, Widget child, {})returnMultiProvider( providers: providers, child: child, ); } / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / two ways of obtaining state / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 1. Getting /// via provider.value <T>(context) is flexible and easy to use. If the Widget is simple, getting the state is a one-line matter. /// project managers who know the business sometimes say, "This, this, and that are just one line of code", /// how many times do we have to work on this line of code, we have to Init, we have to create models, we have to focus on official dynamics, we have to focus on performance, we have to create bugs, well, create bugs? ! static T value<T>(context) {returnProvider.of(context); } // 2. Use Consumer method to obtain /// Static Consumer Connect <T>({Widget Function(BuildContext context, T value, Widget child) Builder, Widget child, }) {returnConsumer<T>(builder: builder, child: child); }}Copy the code

Step 4: Provider initialization and Model data acquisition

runApp(Store.init(child: MyApp()));
Copy the code
@override
Widget build(BuildContext context) {
  print('-->> UserInfo Page Rebuild.'); Widget line = Container(height: 0.2, color: color (0xFF999999), margin: EdgeInsets. FromLTRB (16.0, 0.0, 16.0, 0.0),);return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Store.connect<UserInfoModel>(
          builder: (context, snapshot, child) {
            print('-->> UserInfo Page Info Rebuild.');
            return _buildItem(
              'Avatar',
              () {
                // assets/avatar_0.png
                int toInt = Utils.getRandomNum();
                String toAvatar = 'assets/avatar_$toInt.png'; snapshot.update(avatar: toAvatar); }, text: snapshot.avatar, avatar: snapshot.avatar, ); },),],),); }Copy the code