Наследование виджета путаница
В документации Flutter для InheritedWidget написано
Базовый класс для виджетов, которые эффективно распространяют информацию по дереву.
Чтобы получить ближайший экземпляр определенного типа унаследованного виджета из> контекста сборки, используйте BuildContext.inheritFromWidgetOfExactType.
Унаследованные виджеты, если на них ссылаются таким образом, приведут к перестройке потребителя, когда сам унаследованный виджет изменит свое состояние.
Учитывая, что виджеты во 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
поэтому не может быть переназначен. Предполагая, что этот виджет находится в верхней части дерева, как в большинстве примеров, когда это когда-нибудь будет полезно. Для замены виджета необходимо будет создать новый экземпляр.
Предположительно, где это будет сделано, будет также создан новый экземпляр того, что передано в качестве дочернего элемента, в результате чего потомки этого дочернего элемента также будут перестроены, создав новые экземпляры его дочернего элемента и т. Д.
Окончание с целым деревом восстановлено так или иначе. Таким образом, выборочное обновление применяется с помощью inheritFromWidgetOfExactType
бессмысленно, когда данные экземпляра InheritedWidget никогда не изменятся для этого экземпляра?
Редактировать:
Это самый простой пример того, что я не понимаю, что я могу собрать. В этом примере единственный способ "изменить" InheritedWidget/FrogColor
который находится рядом с корнем приложения, должен иметь своего родителя (MyApp
) восстановить. Это заставляет его восстанавливать своих детей и создавать новый экземпляр FrogColor
и который получает новый дочерний экземпляр. Я не вижу другого пути, чтобы InheritedWidget/FrogColor
изменит свое состояние как в документации
... заставит потребителя перестроиться, когда сам унаследованный виджет изменит состояние.
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, также бессмысленно.
Edit2:
В ответ на ответ @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. Здесь у нее есть поддерево, созданное и удерживаемое как часть государства. В ее случае 2 StatelessColorfulTile() виджеты.
1 ответ
Предположительно, где это будет сделано, будет также создан новый экземпляр того, что передается в качестве потомка, в результате чего потомки этого потомка также будут перестраиваться, создавая новые экземпляры его потомков и т. Д.
Окончание с целым деревом восстановлено так или иначе.
Вот откуда твоя путаница
Перестройка виджета не заставляет его потомков перестраиваться.
Когда родитель перестраивает, фреймворк внутренне проверяет, newChild == oldChild
, в этом случае ребенок не восстановлен.
Таким образом, если экземпляр виджета не изменился или переопределяет operator==
тогда виджет может не перестраиваться при обновлении его родителя.
Это также одна из причин, почему AnimatedBuilder
предложить child
имущество:
AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Container(child: child,);
},
child: Text('Hello world'),
);
Это гарантирует, что при всей продолжительности анимации child
сохраняется и, следовательно, не перестраивается. Приводит к гораздо более оптимизированному интерфейсу.