Flutter プロバイダーの状態管理 - 4 種類の消費者の使用状況の分析

プロバイダーの

Provider.of<T>(context)これは、私たちが提供する静的メソッドです。このメソッドを使用して値を取得すると、見つかった最も近いProviderが返され、コンポーネント ツリー全体を横断することはありません。以下のコードを見てみましょう:Tprovider

ステップ 1: モデルを定義する

モデルを定義しCountNotifier1、次のすべてのサンプル コードをこのモデルに基づいて説明します。

import 'package:flutter/material.dart';

class CountNotifier1 with ChangeNotifier {

  int count = 0;

  void increment() {
    count++;
    notifyListeners();
  }
}

ステップ2:アプリケーションエントリー設定 

return ChangeNotifierProvider(
  create: (_) => CountNotifier1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ConsumerExample(),
  ),
);

ステップ 3: Provider.of を使用する

ここでは値を読み込んで+1ボタンをクリックすることProvider.of<T>()で取得して使用します。

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/consumer_example/count_notifier1.dart';
import 'package:provider/provider.dart';

class ConsumerExample extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("ConsumerExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(Provider.of<CountNotifier1>(context).count.toString(),
              style: TextStyle(
                  color: Colors.red,
                  fontSize: 50
              ),
            ),
            Padding(
              padding: EdgeInsets.only(
                top: 20
              ),
              child: ElevatedButton(
                onPressed: (){
                  Provider.of<CountNotifier1>(context).increment();
                },
                child: Text("点击加1"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

エラーログ

Widgetコードを実行すると、ツリーの外側からプロバイダによって公開された値をリッスンしようとしていることを示すエラーが表示されます。これを修正したい場合は、 に変更できますlistenfalseこの問題は実際にProvider 4.0.2発生します。後ほど、最も重要なことはデフォルトの動作ですture。エラー ログは次のとおりです。

======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<CountNotifier1>(context, listen: false);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

The context used was: ConsumerExample(dependencies: [_InheritedProviderScope<CountNotifier1?>])
'package:provider/src/provider.dart':
Failed assertion: line 276 pos 7: 'context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate'

When the exception was thrown, this was the stack: 
........
====================================================================================================

listen を false に設定する

Provider.of<CountNotifier1>(context, listen: false).increment();

消費者

ConsumberWidgetこれはで呼び出されるだけでありPrvoider.of、その構築はコンストラクターに委任されます。たとえばBuilderWidget複数のモデルに依存する場合、便利な呼び出しも提供されます。次に、これを使用してConsumer23456上記のケースConsumerを変更します

Consumer でコンポーネントをラップする

コンストラクタが入っているので、以下のようbuilderに変更して再実行すると結果は同じになりますが、使うたびに大量の繰り返しコードを書く必要はありません。bodyProvider.ofProvider.of

そこには 3 つのプロパティがあります。

  • コンテキスト: 現在のコンテキスト
  • **Provider.of(context):** モデル オブジェクト
  • child: 子コンポーネント (更新する必要のない部分)
    body: Consumer(
      builder: (_, CountNotifier1 countNotifier1, child) {
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(countNotifier1.count.toString(),
                   style: TextStyle(
                     color: Colors.red,
                     fontSize: 50
                   ),
                  ),
              Padding(
                padding: EdgeInsets.only(
                  top: 20
                ),
                child: ElevatedButton(
                  onPressed: (){
                    countNotifier1.increment();
                  },
                  child: Text("点击加1"),
                ),
              )
            ],
          ),
        );
      },
    ),
    

    消費者の最適化

    最適化方法 1: Consumer の位置を可能な限り調整する

    上記のコードでは、コンポーネントもラップされているCenterことがわかりましたが、これら 2 つのコンポーネントは状態を更新する必要がなく、ビルドするたびに全体をリビルドするため、コード構造が最適化されます。次のようになります。ColumnConsumerWidgetbody

    body: Center(
      child: Consumer(
        builder: (_, CountNotifier1 countNotifier1, child) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  countNotifier1.count.toString(),
                  style: TextStyle(color: Colors.red, fontSize: 50),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () {
                      countNotifier1.increment();
                    },
                    child: Text("点击加1"),
                  ),
                ),
           			Container(
                  child: Column(
                    children: [
                      Text("更多组件1"),
                      Text("更多组件2"),
                      Text("更多组件3"),
                      Text("更多组件4"),
                      Text("更多组件5"),
                      Text("更多组件6"),
                    ],
                  ),
                )
              ],
            ),
          );
        },
      )
    )

    最適化方法 2: 更新する必要はないが Consumer によってラップされるコンポーネントには子を使用します

    たとえば、上記には状態を更新する必要のないコンポーネント 1 ~ 6、または数百ものコンポーネントがありますが、パッケージを使用してConsumerそれらすべてを更新するため、明らかにパフォーマンスの低下につながると考えるかもしれません。複数のパッケージだけで更新する必要があるコンポーネントConsumer数の問題は解決しましたが、これは本末転倒ではありませんか?Providerコードを解決するための堅牢で反復的なコードなので、現時点では、Consumer提供されたchildパラメーターを使用できます。 、 次のように:

    body: Center(
      child: Consumer(
        builder: (_, CountNotifier1 countNotifier1, child) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  countNotifier1.count.toString(),
                  style: TextStyle(color: Colors.red, fontSize: 50),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () {
                      countNotifier1.increment();
                    },
                    child: Text("点击加1"),
                  ),
                ),
                child!
              ],
            ),
          );
        },
        child: Container(
          child: Column(
            children: [
              Text("更多组件1"),
              Text("更多组件2"),
              Text("更多组件3"),
              Text("更多组件4"),
              Text("更多组件5"),
              Text("更多组件6"),
            ],
          ),
        ),
      )
    ),

    セレクタ

    SelectorクラスはConsumer似ていますが、メソッドをbuild呼び出すときにより詳細な制御を提供します。簡単に言うと、クラスはコンシューマでもあり、モデルから定義するプロパティを準備できます。WidgetSelector

    例を挙げてみましょう:

    例えば、ユーザーモデルには50個の属性がありますが、更新する必要があるのは年齢だけなので、ユーザー名電話番号などのコンポーネントを再構築する必要がないので、Selectorこの問題の解決に使用してみましょう。例を見てみましょう:

    第一步:定义模型
    import 'package:flutter/material.dart';
    
    class UserModel6 with ChangeNotifier {
    
      String name = "Jimi";
      int age = 18;
      String phone = "18888888888";
    
    
      void increaseAge() {
        age++;
        notifyListeners();
      }
    }
    第二步:应用程序入口设置
    return ChangeNotifierProvider(
      create: (_) => UserModel6(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: SelectorExample(),
      ),
    );
    第三步:使用Selector更精细的控制
    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/selector_example/user_model6.dart';
    import 'package:provider/provider.dart';
    
    class SelectorExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("SelectorExample"),
          ),
          body: Center(
            child: Selector<UserModel6, int>(
              selector: (_, userModel6) => userModel6.age,
              builder: (_, age, child) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(age.toString(),
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    ),
                    child!
                  ],
                );
              },
              child: Padding(
                padding: EdgeInsets.all(20),
                child: ElevatedButton(
                  onPressed: (){
                    Provider.of<UserModel6>(context, listen: false).increaseAge();
                  },
                  child: Text("改变年龄"),
                ),
              ),
            ),
          ),
        );
      }
    }

    3 つの方法:

  • BuildContext.read: BuildContext.read<CountNotifier1>()は置き換えることができProvider.of<CountNotifier1>(context,listen: false)CountNotifier1それを見つけて返します。
  • BuildContext.watch: BuildContext.watch<CountNotifier1>()置き換えることができますProvider.of<CountNotifier1>(context,listen: false)。見た目はread同じですが、watch使用する必要はありませんConsumer
  • BuildContext.select: BuildContext.select<CountNotifier1>()置き換えることができProvider.of<CountNotifier1>(context,listen: false)、見た目はwatch同じですが、select使用する必要はありませんSelector
  • BuildContext.read

    次の 2 つの結果の使用方法は同じです

    Provider.of() を使用する

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
    
          /// Provider.of 获取值
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(Provider.of<CountNotifier2>(context).count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    BuildContext.readを使用する

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// read 获取值
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(context.read<CountNotifier2>().count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    BuildContext.watch

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
    
        /// 重要
        final countNotifier2 = context.watch<CountNotifier2>();
    
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// watch 
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(countNotifier2.count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => countNotifier2.increment(),
                    child: Text("点击加1"),
                  ),
                ),
              ],
            ),
          ),
    
        );
      }
    }
    

    BuildContext.select

    セレクターを使用する

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// Selector
          body: Center(
            child: Selector<CountNotifier2, int>(
              selector: (_, countNotifier2) => countNotifier2.count,
              builder: (_, count, child) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(count.toString(),
                      style: TextStyle(
                          color: Colors.red,
                          fontSize: 50
                      ),
                    ),
                    child!
                  ],
                );
              },
              child: Padding(
                padding: EdgeInsets.only(top: 20),
                child: ElevatedButton(
                  onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                  child: Text("点击加1"),
                ),
              ),
            ),
          ),
    
        );
      }
    }

    BuildContext.select を使用する

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/Inherited_context_example/count_notifier2.dart';
    import 'package:provider/provider.dart';
    
    
    class InheritedContextExample extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        
        /// 重要
        final count = context.select((CountNotifier2 countNotifier2) => countNotifier2.count);
    
        return Scaffold(
          appBar: AppBar(
            title: Text("InheritedContextExample"),
          ),
          /// select
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(count.toString(),
                  style: TextStyle(
                      color: Colors.red,
                      fontSize: 50
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: ElevatedButton(
                    onPressed: () => Provider.of<CountNotifier2>(context, listen: false).increment(),
                    child: Text("点击加1"),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }

    要約する

    Flutter値を読み取るためのさまざまな方法が提供されます。上記では、消費者の 4 つのカテゴリを使用、分析、比較しています。実際のアプリケーション シナリオに応じて、対応する方法を使用できます。

おすすめ

転載: blog.csdn.net/RreamigOfGirls/article/details/130064948