Наследование виджета путаница

В документации 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 сохраняется и, следовательно, не перестраивается. Приводит к гораздо более оптимизированному интерфейсу.

Другие вопросы по тегам