プロバイダーの
Provider.of<T>(context)
これは、私たちが提供する静的メソッドです。このメソッドを使用して値を取得すると、見つかった最も近い型Provider
が返され、コンポーネント ツリー全体を横断することはありません。以下のコードを見てみましょう:T
provider
ステップ 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
コードを実行すると、ツリーの外側からプロバイダによって公開された値をリッスンしようとしていることを示すエラーが表示されます。これを修正したい場合は、 に変更できますlisten
。false
この問題は実際に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();
消費者
Consumber
Widget
これはで呼び出されるだけでありPrvoider.of
、その構築はコンストラクターに委任されます。たとえばBuilder
、Widget
複数のモデルに依存する場合、便利な呼び出しも提供されます。次に、これを使用してConsumer23456
上記のケースConsumer
を変更します
Consumer でコンポーネントをラップする
コンストラクタが入っているので、以下のようbuilder
に変更して再実行すると結果は同じになりますが、使うたびに大量の繰り返しコードを書く必要はありません。body
Provider.of
Provider.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 つのコンポーネントは状態を更新する必要がなく、ビルドするたびに全体をリビルドするため、コード構造が最適化されます。次のようになります。Column
Consumer
Widget
body
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
呼び出すときにより詳細な制御を提供します。簡単に言うと、クラスはコンシューマでもあり、モデルから定義するプロパティを準備できます。Widget
Selector
例を挙げてみましょう:
例えば、ユーザーモデルには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 つのカテゴリを使用、分析、比較しています。実際のアプリケーション シナリオに応じて、対応する方法を使用できます。