使用 Provider 的 context.read<T>() 方法触发小部件重建
Triggering Widget Rebuilds with Provider's context.read<T>() Method
根据 Flutter 的文档和 this example,据我所知,Provider 包的 context.read<T>
和 context.watch<T>
方法之间的一个关键区别与触发小部件重建有关。您可以在任何小部件的 build
方法中调用 context.watch<T>()
来访问当前状态,并在状态发生变化时要求 Flutter 重建您的小部件。您不能在构建方法之外使用 context.watch<T>()
,因为这通常会导致细微的错误。相反,他们说,使用 context.read<T>()
,它获取当前状态但不会要求 Flutter 进行未来的重建。
我尝试制作这个简单的应用程序:
class MyDataNotifier extends ChangeNotifier {
String _testString = 'test';
// getter
String get testString => _testString;
// update
void updateString(String aString) {
_testString = aString;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => MyDataNotifier(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(context.read<MyDataNotifier>().testString),
),
body: Container(
child: Level1(),
),
),
);
}
}
class Level1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
onChanged: (val) {
context.read<MyDataNotifier>().updateString(val);
},
),
Text(context.read<MyDataNotifier>().testString),
],
);
}
}
所有电话都打给counter.read<T>()
。应用程序的状态发生变化,但 UI 未使用新值重建。我必须将其中一个调用更改为 counter.watch<T>()
以使状态重建。
另一方面,在 DZone's simple example 中,UI 重建,所有调用都是 context.read()。
他们的代码和我的有什么不同?为什么我不能用 counter.read() 调用重建?
TLDR:快速浏览后,DZone 文章看起来有错误。
更长的答案
context.watch<Foo>()
做了两件事:
- return 来自树的状态实例
- 将
context
标记为依赖于 Foo
context.read<Foo>()
只做 1).
每当您的 UI 依赖于 Foo
时,您应该使用 context.watch
,因为这会适当地通知 Flutter 该依赖项,并且它会被正确地重建。
一般来说,归结为这条经验法则:
- 在
build()
方法中使用 context.watch
,或 return 是 Widget
的任何其他方法
- 在
onPressed
处理程序(和其他相关函数)中使用 context.read
人们似乎不恰当地使用 context.read
的主要原因是出于性能原因。一般来说,context.read
优于 context.watch
的性能是 反模式 。相反,如果您想限制小部件重建的频率,您应该使用 context.select
。当您有一个经常变化的值时,这是最有用的。
假设您处于以下状态:
class FooState extends ChangeNotifier {
// imagine this us updated very often
int millisecondsSinceLastTap;
// updated less often
bool someOtherProperty = false;
}
如果您有一个显示 someOtherProperty
的小部件,context.watch
可能会导致许多不必要的重建。相反,您可以使用 context.select
仅依赖于状态的已处理部分:
// read the property, rebuild only when someOtherProperty changes
final property = context.select((FooState foo) => foo.someOtherProperty);
return Text('someOtherProperty: $property');
即使值经常更新,如果提供给 select 的函数的输出没有改变,小部件也不会重建:
// even though millisecondsSinceLastTap may be updating often,
// this will only rebuild when millisecondsSinceLastTap > 1000 changes
final value = context.select((FooState state) => state.millisecondsSinceLastTap > 1000);
return Text('${value ? "more" : "less"} than 1 second...');
根据 Flutter 的文档和 this example,据我所知,Provider 包的 context.read<T>
和 context.watch<T>
方法之间的一个关键区别与触发小部件重建有关。您可以在任何小部件的 build
方法中调用 context.watch<T>()
来访问当前状态,并在状态发生变化时要求 Flutter 重建您的小部件。您不能在构建方法之外使用 context.watch<T>()
,因为这通常会导致细微的错误。相反,他们说,使用 context.read<T>()
,它获取当前状态但不会要求 Flutter 进行未来的重建。
我尝试制作这个简单的应用程序:
class MyDataNotifier extends ChangeNotifier {
String _testString = 'test';
// getter
String get testString => _testString;
// update
void updateString(String aString) {
_testString = aString;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => MyDataNotifier(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(context.read<MyDataNotifier>().testString),
),
body: Container(
child: Level1(),
),
),
);
}
}
class Level1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
onChanged: (val) {
context.read<MyDataNotifier>().updateString(val);
},
),
Text(context.read<MyDataNotifier>().testString),
],
);
}
}
所有电话都打给counter.read<T>()
。应用程序的状态发生变化,但 UI 未使用新值重建。我必须将其中一个调用更改为 counter.watch<T>()
以使状态重建。
另一方面,在 DZone's simple example 中,UI 重建,所有调用都是 context.read()。
他们的代码和我的有什么不同?为什么我不能用 counter.read() 调用重建?
TLDR:快速浏览后,DZone 文章看起来有错误。
更长的答案
context.watch<Foo>()
做了两件事:
- return 来自树的状态实例
- 将
context
标记为依赖于Foo
context.read<Foo>()
只做 1).
每当您的 UI 依赖于 Foo
时,您应该使用 context.watch
,因为这会适当地通知 Flutter 该依赖项,并且它会被正确地重建。
一般来说,归结为这条经验法则:
- 在
build()
方法中使用context.watch
,或 return 是Widget
的任何其他方法
- 在
onPressed
处理程序(和其他相关函数)中使用context.read
人们似乎不恰当地使用 context.read
的主要原因是出于性能原因。一般来说,context.read
优于 context.watch
的性能是 反模式 。相反,如果您想限制小部件重建的频率,您应该使用 context.select
。当您有一个经常变化的值时,这是最有用的。
假设您处于以下状态:
class FooState extends ChangeNotifier {
// imagine this us updated very often
int millisecondsSinceLastTap;
// updated less often
bool someOtherProperty = false;
}
如果您有一个显示 someOtherProperty
的小部件,context.watch
可能会导致许多不必要的重建。相反,您可以使用 context.select
仅依赖于状态的已处理部分:
// read the property, rebuild only when someOtherProperty changes
final property = context.select((FooState foo) => foo.someOtherProperty);
return Text('someOtherProperty: $property');
即使值经常更新,如果提供给 select 的函数的输出没有改变,小部件也不会重建:
// even though millisecondsSinceLastTap may be updating often,
// this will only rebuild when millisecondsSinceLastTap > 1000 changes
final value = context.select((FooState state) => state.millisecondsSinceLastTap > 1000);
return Text('${value ? "more" : "less"} than 1 second...');