4

InheritedWidget的 Flutter文档说

小部件的基类,可有效地沿树向下传播信息。

要从 > 构建上下文获取特定类型的继承小部件的最近实例,请使用 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因此无法重新分配。假设这个小部件就在树的顶部,就像在大多数示例中一样,它什么时候会有用。对于要替换的小部件,必须创建一个新实例。

大概在这样做的地方,也将创建一个作为孩子传递的新实例,导致该孩子的后代也重建,创建其孩子的新实例等。

最终还是重建了整棵树。那么通过 using 应用的选择性更新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 中完成的注册也毫无意义。

编辑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 说将子树存储为状态的一部分是不正确的,但我不认为有任何理由认为这是不正确的,事实上他们在一些谷歌教程视频中这样做了。在这里,她创建了一个子树并作为状态的一部分保存。在她的案例中,有 2 个 StatelessColorfulTile() 小部件。

4

1 回答 1

2

大概在这样做的地方,也将创建一个作为孩子传递的新实例,导致该孩子的后代也重建,创建其孩子的新实例等。

最终还是重建了整棵树。

这就是你的困惑来自哪里

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

当父级重建时,框架会在内部检查 if newChild == oldChild,在这种情况下不会重建子级。

因此,如果小部件的实例没有更改,或者如果它 operator==被覆盖,那么小部件可能在其父级更新时不会重建。

AnimatedBuilder这也是提供child房产的原因之一:

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

这确保了在动画的整个持续时间内,何时child保留,因此不会重建。导致更优化的用户界面。

于 2019-02-02T16:17:44.443 回答