InheritedWidget混乱

时间:2019-02-02 15:16:31

标签: dart flutter inherited-widget

在颤振documentation for 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教程视频中这样做。 Here她创建了一个子树并将其保留为州的一部分。在她的情况下,有2个StatelessColorfulTile()小部件。

1 个答案:

答案 0 :(得分:0)

  

据推测,这里做到这一点,无论是通过作为一个孩子也将被创建,引起孩子的后代的新实例也重建,创造了儿童的新实例等。

     

最后还是重建了整个树。

那是你困惑的来源

重建小部件不会强制其后代重建。

父级重建时,框架会内部检查newChild == oldChild是否为{em> not 子级。

这样,如果小部件的实例没有更改,或者它覆盖了  operator==,则在更新其父项时,可能无法重建该窗口小部件。

这也是AnimatedBuilder提供child属性的原因之一:

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

这可确保在动画的整个过程中child被保留,因此不会被重建。导致更加优化的用户界面。