1. 程式人生 > 其它 >provider 跨元件狀態管理

provider 跨元件狀態管理

provider 跨元件狀態管理

Provider 包是由 Remi Rousselet 建立旨在儘可能快速地處理狀態。在 Provider 中,小部件會監聽狀態的變化,並在收到通知後立即更新。

因此,當有狀態改變時,而不是重建整個 widget 樹,只改變受影響的 widget,從而減少工作量並使應用程式執行得更快更流暢。

一、原理

Model變化後會自動通知ChangeNotifierProvider(訂閱者),ChangeNotifierProvider內部會重新構建InheritedWidget,而依賴該InheritedWidget的子孫Widget就會更新。

原有普遍方式:通過引數傳遞資料,並setState實時更新組建;

使用Provider益處:

    1. 業務程式碼更關注資料,只要更新Model,則UI會自動更新,而不用在狀態改變後再去手動呼叫setState()來顯式更新頁面。

    2. 資料改變的訊息傳遞被遮蔽了,我們無需手動去處理狀態改變事件的釋出和訂閱了,這一切都被封裝在Provider中了。這真的很棒,幫我們省掉了大量的工作!

    3. 在大型複雜應用中,尤其是需要全域性共享的狀態非常多時,使用Provider將會大大簡化我們的程式碼邏輯,降低出錯的概率,提高開發效率。

二、狀態管理方式

1、Provider 方式

provider 不需要被監聽,有的常量或者方法,根本不需要“牽一髮而動全身”,也就是說他們不會被要求隨著變動而變動,這樣的需求使用Provider;

最基本的狀態管理方式,以一個引數方式繫結和展示;

1) 繫結單條資料

Provider 可在需要的 Widget 處進行資料繫結;

基本單條資料繫結:

當我們確定繫結的資料型別時,建議繫結時新增資料型別,如:Provider<String>.value( value: '', child:);不確定型別Provider.value( value: '', child:)

Column(
    children: [
        Provider<String>.value( value: 'Provider 基礎傳值', child: const BaseValueWidget()),
    ],
) 

2)獲取資料

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

  @override
  Widget build(BuildContext context) {
    return Text(Provider.of<String>(context));
  }
}

 3)繫結多條資料

 Column(
  children: [
    /// 單條傳值
    Provider<String>.value(
        value: 'Provider 基礎傳值', child: const BaseValueWidget()),

    /// 多條傳值 UserModel實體
    //巢狀繫結
    Provider<UserModel>.value(
        value: UserModel('巢狀繫結', 18),
        child: Provider<int>.value(
            value: 20,
            child: Provider<bool>.value(
                value: false, child: const NestingWidget()))),
    // 聚合方式 推薦
    MultiProvider(providers: [
      Provider<UserModel>.value(value: UserModel('聚合方式', 10)),
      Provider<int>.value(value: 11),
      Provider<bool>.value(value: false)
    ], child: const NestingWidget()),
  ],
)

 

class UserModel {
  String name;
  int age;
  UserModel(this.name, this.age);
}

獲取資料

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

  @override
  Widget build(BuildContext context) {
    return Text(
        'Provider: '
        '${Provider.of<int>(context)} | ${Provider.of<bool>(context)} | ${Provider.of<UserModel>(context).name} |${Provider.of<UserModel>(context).name = 'Hello World!'}',
        style: const TextStyle(color: Colors.redAccent));
  }
}
Provider 繫結資料型別比較靈活,並非只是基本資料型別,這裡定義了一個 UserModel 類,可正常狀態管理;獲取 name 後重新設定 name 之後獲取的 UserModel 為最新的資料;

4)作用域

獲取繫結資料的範圍是在繫結資料的子 Widget 中;

2、ChangeNotifierProvider 方式

ChangeNotifierProvider 它會隨著某些資料改變而被通知更新,也就是說,比如這個 Model 被用在多個 page,那麼當其中一處被改變時,他就應該告訴其他的地方,改更新了,這樣的需求就使用ChangeNotifierProvider;

通過呼叫 ChangeNotifier.notifyListenersChangeNotifier 進行監聽,將其公開給它的子 Widget 並重建依賴項;

1) 繫結資料

ChangeNotifierProvider 繫結資料有兩種方式:
ChangeNotifierProvider({Key key, @required ValueBuilder builder, Widget child });通過構造器建立一個 ChangeNotifier,在 ChangeNotifierProvider 移除時自動處理;
ChangeNotifierProvider(
  create: (_) => PersonChangeNotifier('ChangeNotifier方式1', 11),
  child: const NotifierWidget(),
),
ChangeNotifierProvider.value({Key key, @required T notifier, Widget child }) 通過監聽通知給子 Widget 並重建依賴項;
ChangeNotifierProvider<PersonChangeNotifier>.value(
  value: PersonChangeNotifier('ChangeNotifier方式2', 1),
  child: const NotifierWidget(),
)
class PersonChangeNotifier with ChangeNotifier {
  String name;
  int age;
  PersonChangeNotifier(this.name, this.age);
  updateName(String name) {
    this.name = name;
    notifyListeners();
  }
}

2) 獲取資料

獲取資料的方式與直接使用 Provider 相似;

Text(Provider.of<PersonChangeNotifier>(context).name);

相對於 ProviderChangeNotifierProvider 方式更加靈活,可以通過重寫 get/set 方法來對狀態管理進行修改和使用;

3)更新資料

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(Provider.of<PersonChangeNotifier>(context).name),
        InkWell(
          child: const Text('點選更新name'),
          onTap: () {
            context.read<PersonChangeNotifier>().updateName('name');
          },
        ),
      ],
    );
  }
}

3、ChangeNotifierProxyProvider

ChangeNotifierProxyProvider 它不僅要像ChangeNotifierProvider一樣,通知更新,還要協調 Model 與 Model 之間的更新,比如一個 ModelA 依賴另一個 ModelB,ModelB 更新,他就要讓依賴它的 ModelA 也隨之更新,這就是使用ChangeNotifierProxyProvider; https://pub.flutter-io.cn/documentation/provider/latest/provider/ChangeNotifierProxyProvider-class.html
ChangeNotifierProvider(
  create: (context) {
    return MyChangeNotifier(
      myModel: Provider.of<MyModel>(context, listen: false),
    );
  },
  child: ...
)

 

三、有選擇地更新狀態

該Consumer控制元件只允許子控制元件,而不在 widget 樹影響其他部件重建,小部件進行更新。

我們通過Text用 a 包裝兩個小部件ColumnbuilderConsumer小部件公開的函式處返回它來實現這一點:

 
Consumer<UserDetailsProvider>(
  builder: (context, provider, child) {
    return Column(
      children: [
        Text(
          'Hi ' + provider.name,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
        Text(
          'You are ' + provider.age.toString() + ' years old',
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.w400,
          ),
        ),
      ],
    );
  },
)