Наследование виджета путаница - PullRequest
0 голосов
/ 02 февраля 2019

В документации 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;
}

свойство 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 Ответ

0 голосов
/ 02 февраля 2019

Предположительно, где это будет сделано, будет также создан новый экземпляр того, что передается в качестве потомка, в результате чего потомки этого потомка также будут перестроены, создавая новые экземпляры его потомков и т. Д.

В конце концов, все дерево перестраивается.

Вот из-за чего возникает путаница

Перестройка виджета не заставляет его потомков перестраиваться.

Когдародительский перестроить, каркас внутренне проверить, если newChild == oldChild, в этом случае дочерний объект не перестроен.

Таким образом, если экземпляр виджета не изменился, или если онпереопределяет operator==, тогда виджет может не перестраиваться при обновлении его родителя.

Это также одна из причин, по которой AnimatedBuilder предлагает свойство child:

AnimatedBuilder(
  animation: animation,
  builder: (context, child) {
    return Container(child: child,);
  },
  child: Text('Hello world'),
);

Это гарантирует, что при всей продолжительности анимации, child сохраняется и, следовательно, не восстанавливается.Приводит к гораздо более оптимизированному интерфейсу.

...