InheritedWidget 混乱
InheritedWidget confusion
Flutter documentation for InheritedWidget 说
Base class for widgets that efficiently propagate information down the tree.
To obtain the nearest instance of a particular type of inherited widget from > a build context, use BuildContext.inheritFromWidgetOfExactType.
Inherited widgets, when referenced in this way, will cause the consumer
to rebuild when the inherited widget itself changes state.
鉴于 Flutter 中的小部件是不可变的,并且在示例代码中..
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
颜色 属性 是 final
,因此无法重新分配。假设这个小部件就在树的顶部,就像在大多数示例中一样,它什么时候有用。对于要替换的小部件,必须创建一个新实例。
大概在这样做的地方,也将创建一个作为子传递的任何东西的新实例,导致该子的后代也重建,创建其 childresn 等的新实例。
最终还是重建了整棵树。因此,当 InheritedWidget 实例的数据永远不会为该实例更改时,使用 inheritFromWidgetOfExactType
应用的选择性更新毫无意义?
编辑:
这是我不明白的可以放在一起的最简单的例子。
在此示例中,“更改”应用程序根附近的 InheritedWidget/FrogColor
的唯一方法是重建其父级 (MyApp
)。这会导致它重建其子实例并创建 FrogColor
的新实例,并向其传递一个新的子实例。我看不到 InheritedWidget/FrogColor
的任何其他方式
会像文档中那样改变它的状态
... will cause the consumer to rebuild when the inherited widget itself changes state.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp>
{
@override
Widget build(BuildContext context) {
var random = Random(DateTime.now().millisecondsSinceEpoch);
return FrogColor(
color : Color.fromARGB(255,random.nextInt(255),random.nextInt(255),random.nextInt(255)),
child:MaterialApp(
title: 'Flutter Demo',
home: Column (
children: <Widget>[
WidgetA(),
Widget1(),
FlatButton(
child:Text("set state",style:TextStyle(color:Colors.white)),
onPressed:() => this.setState((){})
)
]
)
)
);
}
}
class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return WidgetB();
}
}
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Text("SomeText",style:TextStyle(color:FrogColor.of(context).color));
}
}
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Widget2();
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Text("SomeText",style:TextStyle(color:FrogColor.of(context).color));
}
}
此外,这个输出是
I/flutter (24881): Ran Build WidgetA
I/flutter (24881): Ran Build WidgetB
I/flutter (24881): Ran Build Widget1
I/flutter (24881): Ran Build Widget2
所以所有的子部件总是被重建。使在 inheritFromWidgetOfExactType 中完成的注册也毫无意义。
编辑2:
回应@RémiRousselet 在评论中的回答,修改上面的例子,类似
class MyAppState extends State<MyApp>
{
Widget child;
MyAppState()
{
child = MaterialApp(
title: 'Flutter Demo',
home: Column (
children: <Widget>[
WidgetA(),
Widget1(),
FlatButton(
child:Text("set state",style:TextStyle(color:Colors.white)),
onPressed:() => this.setState((){})
)
]
)
);
}
@override
Widget build(BuildContext context) {
var random = Random(DateTime.now().millisecondsSinceEpoch);
return FrogColor(
color : Color.fromARGB(255,random.nextInt(255),random.nextInt(255),random.nextInt(255)),
child: child
);
}
}
通过存储不应在构建函数之外修改的树来工作,以便在每次重建时将相同的子树传递给 InhertedWidget。这确实有效,只会导致重建已向 inheritFromWidgetOfExactType 注册的小部件,但不会重建其他小部件。
虽然@RémiRousselet 说将子树存储为状态的一部分是不正确的,但我不认为有任何理由认为这是不对的,事实上他们在一些 google 教程视频中这样做. Here 她创建了一个子树并将其保存为状态的一部分。在她的案例中有 2 个 StatelessColorfulTile() 小部件。
Presumably where this is done, a new instance of whatever is passed as a child will be created too, causing that child's descendants to also rebuild, creating new instances of its children etc..
Ending up with the whole tree rebuilt anyway.
这就是你困惑的来源
小部件重建不会强制其后代重建。
当父重建时,框架会在内部检查是否 newChild == oldChild
,在这种情况下,子 不会 重建。
因此,如果小部件的实例没有改变,或者如果它覆盖
operator==
则小部件有可能在其父级更新时不重建。
这也是AnimatedBuilder
提供child
的原因之一属性:
AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Container(child: child,);
},
child: Text('Hello world'),
);
这可确保在整个动画持续时间内,child
得到保留,因此不会重建。导致更加优化 UI.
Flutter documentation for InheritedWidget 说
Base class for widgets that efficiently propagate information down the tree.
To obtain the nearest instance of a particular type of inherited widget from > a build context, use BuildContext.inheritFromWidgetOfExactType.
Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.
鉴于 Flutter 中的小部件是不可变的,并且在示例代码中..
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
颜色 属性 是 final
,因此无法重新分配。假设这个小部件就在树的顶部,就像在大多数示例中一样,它什么时候有用。对于要替换的小部件,必须创建一个新实例。
大概在这样做的地方,也将创建一个作为子传递的任何东西的新实例,导致该子的后代也重建,创建其 childresn 等的新实例。
最终还是重建了整棵树。因此,当 InheritedWidget 实例的数据永远不会为该实例更改时,使用 inheritFromWidgetOfExactType
应用的选择性更新毫无意义?
编辑:
这是我不明白的可以放在一起的最简单的例子。
在此示例中,“更改”应用程序根附近的 InheritedWidget/FrogColor
的唯一方法是重建其父级 (MyApp
)。这会导致它重建其子实例并创建 FrogColor
的新实例,并向其传递一个新的子实例。我看不到 InheritedWidget/FrogColor
的任何其他方式
会像文档中那样改变它的状态
... will cause the consumer to rebuild when the inherited widget itself changes state.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp>
{
@override
Widget build(BuildContext context) {
var random = Random(DateTime.now().millisecondsSinceEpoch);
return FrogColor(
color : Color.fromARGB(255,random.nextInt(255),random.nextInt(255),random.nextInt(255)),
child:MaterialApp(
title: 'Flutter Demo',
home: Column (
children: <Widget>[
WidgetA(),
Widget1(),
FlatButton(
child:Text("set state",style:TextStyle(color:Colors.white)),
onPressed:() => this.setState((){})
)
]
)
)
);
}
}
class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return WidgetB();
}
}
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Text("SomeText",style:TextStyle(color:FrogColor.of(context).color));
}
}
class Widget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Widget2();
}
}
class Widget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Ran Build ${this.runtimeType.toString()}");
return Text("SomeText",style:TextStyle(color:FrogColor.of(context).color));
}
}
此外,这个输出是
I/flutter (24881): Ran Build WidgetA
I/flutter (24881): Ran Build WidgetB
I/flutter (24881): Ran Build Widget1
I/flutter (24881): Ran Build Widget2
所以所有的子部件总是被重建。使在 inheritFromWidgetOfExactType 中完成的注册也毫无意义。
编辑2:
回应@RémiRousselet 在评论中的回答,修改上面的例子,类似
class MyAppState extends State<MyApp>
{
Widget child;
MyAppState()
{
child = MaterialApp(
title: 'Flutter Demo',
home: Column (
children: <Widget>[
WidgetA(),
Widget1(),
FlatButton(
child:Text("set state",style:TextStyle(color:Colors.white)),
onPressed:() => this.setState((){})
)
]
)
);
}
@override
Widget build(BuildContext context) {
var random = Random(DateTime.now().millisecondsSinceEpoch);
return FrogColor(
color : Color.fromARGB(255,random.nextInt(255),random.nextInt(255),random.nextInt(255)),
child: child
);
}
}
通过存储不应在构建函数之外修改的树来工作,以便在每次重建时将相同的子树传递给 InhertedWidget。这确实有效,只会导致重建已向 inheritFromWidgetOfExactType 注册的小部件,但不会重建其他小部件。
虽然@RémiRousselet 说将子树存储为状态的一部分是不正确的,但我不认为有任何理由认为这是不对的,事实上他们在一些 google 教程视频中这样做. Here 她创建了一个子树并将其保存为状态的一部分。在她的案例中有 2 个 StatelessColorfulTile() 小部件。
Presumably where this is done, a new instance of whatever is passed as a child will be created too, causing that child's descendants to also rebuild, creating new instances of its children etc..
Ending up with the whole tree rebuilt anyway.
这就是你困惑的来源
小部件重建不会强制其后代重建。
当父重建时,框架会在内部检查是否 newChild == oldChild
,在这种情况下,子 不会 重建。
因此,如果小部件的实例没有改变,或者如果它覆盖
operator==
则小部件有可能在其父级更新时不重建。
这也是AnimatedBuilder
提供child
的原因之一属性:
AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Container(child: child,);
},
child: Text('Hello world'),
);
这可确保在整个动画持续时间内,child
得到保留,因此不会重建。导致更加优化 UI.