flutter概念介绍之Widget - State- BuildContext - InheritedWidget

本文介绍了Flutter应用程序中Widget,State,BuildContext和InheritedWidget的重要概念。

特别注意InheritedWidget,它是最重要且记录较少的小部件之一。

本文内容很长,但做技术就是要沉得下心!
难度:初学者

前言

Flutter中Widget,State和BuildContext的概念是每个Flutter开发人员需要完全理解的最重要概念之一。
但是,文档很庞大,并不总是清楚地解释这个概念。

我会用自己的话语和捷径来解释这些概念,本文的真正目的是试图澄清以下主题:

  • 有状态和无状态小部件之间的区别
  • 什么是BuildContext
  • 什么是State以及如何使用它
  • BuildContext与其State对象之间的关系
  • InheritedWidget以及在Widgets树中传播信息的方式
  • 重建的概念

第1部分:概念

小工具的概念

在Flutter中,几乎所有东西都是Widget。

将Widget视为可视组件(或与应用程序的可视方面交互的组件)。

当您需要构建与布局直接或间接相关的任何内容时,您正在使用窗口小部件。

小部件树的概念

窗口小部件以树形结构组织。

包含其他小部件的小部件称为父Widget(或Widget容器)。

包含在父窗口小部件中的窗口小部件称为子窗口小部件。

让我们用Flutter自动生成的基本应用程序来说明这一点。

这是简化的代码,仅限于构建方法:

@override
Widget build(BuildContext){
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
}

如果我们现在观察这个基本示例,我们将看到以下Widgets树结构(限制代码中存在的Widgets列表):
[站外图片上传中...(image-c411a3-1539427274357)]

BuildContext的概念

另一个重要的概念是BuildContext。

BuildContext只不过是对构建的所有窗口小部件的树结构中的窗口小部件的位置的引用。

简而言之,将BuildContext视为Widgets树的一部分,Widget将附加到此树。

一个BuildContext只属于一个小部件。

如果窗口小部件“A”具有子窗口小部件,则窗口小部件“A”的BuildContext将成为直接子窗口BuildContexts的父BuildContext。

阅读本文,很明显BuildContexts是链接的,并且正在组成BuildContexts树(父子关系)。

如果我们现在尝试在上图中说明BuildContext的概念,我们可以看到(仍然是一个非常简化的视图),其中每种颜色代表一个BuildContext(除了MyApp,它是不同的):
[站外图片上传中...(image-3be46-1539427274357)]

BuildContext可见性(简化语句):
“ Something ”仅在其自己的BuildContext或其父BuildContext的BuildContext中可见。

从这个语句我们可以从子BuildContext派生出来,很容易找到一个祖先(= parent)Widget。

一个例子是,考虑Scaffold> Center> Column> Text:
context.ancestorWidgetOfExactType(Scaffold)=>通过从Text上下文转到树结构来返回第一个Scaffold。

从父BuildContext,也可以找到一个后代(=子)Widget,但不建议这样做(我们稍后会讨论)


小部件的类型

小部件有两种类型:

无状态小工具 Stateless Widget

这些可视组件中的一些除了它们自己的配置信息之外不依赖于任何其他信息,该信息在其直接父级构建时提供。

换句话说,这些小部件一旦创建就不必关心任何变化。

这些小部件称为无状态小部件。

这种小部件的典型示例可以是Text,Row,Column,Container ......其中,在构建时,我们只是将一些参数传递给它们。

参数可以是装饰,尺寸甚至其他小部件中的任何内容。不要紧。唯一重要的是这个配置一旦应用,在下一个构建过程之前不会改变。

无状态窗口小部件只能在加载/构建窗口小部件时绘制一次,这意味着无法基于任何事件或用户操作重绘窗口小部件。

无状态小部件生命周期

以下是与无状态小组件相关的代码的典型结构。

如您所见,我们可以将一些额外的参数传递给它的构造函数。但是,请记住,这些参数不会在稍后阶段发生变化(变异),只能按原样使用

class MyStatelessWidget extends StatelessWidget {
    MyStatelessWidget({
        Key key,
        this.parameter,
    }): super(key:key);

    final parameter;

    @override
    Widget build(BuildContext context){
        return new ...
    }
}

即使有另一种方法可以被重写(createElement),后者几乎从不被重写。
唯一需要被重写的是build

这种无状态小部件的生命周期很简单:

  • 初始化
  • 通过build()渲染

有状态的小工具 Stateful Widget

其他一些小部件将处理一些在Widget生命周期内会发生变化的内部数据。因此,该数据变得动态。

此Widget保存的数据集可能会在此Widget的生命周期内发生变化,称为State。

这些窗口小部件称为有状态窗口小部件(Stateful Widget)。

这样的Widget的示例可以是用户可以选择的复选框列表或者根据条件禁用的Button。

State的概念

State定义StatefulWidget实例的“行为”部分。
它包含旨在与Widget交互/干扰的信息:

  • 行为
  • 布局

应用于状态的任何更改都会强制Widget重建。

State和BuildContext之间的关系

对于有状态窗口小部件,状态与BuildContext关联。

此关联是永久性的 ,State对象永远不会更改其BuildContext。
即使可以在树结构周围移动Widget BuildContext,State仍将与该BuildContext保持关联。

当State与BuildContext关联时,State被视为已挂载

重点:
由于State对象与BuildContext相关联,这意味着State对象不能(直接)通过另一个BuildContext访问!(我们将在稍后讨论这个问题)。


有状态的小部件Stateful Widget 的生命周期

这是与Stateful Widget相关的典型代码结构。

由于本文的主要目的是用“变量”数据来解释State的概念,我将故意跳过与某些Stateful Widget overridable方法相关的任何解释,这些方法与此没有特别的关系。

这些可覆盖的方法是didUpdateWidget,deactivate,reassemble。这些将在另一篇文章中讨论。

class MyStatefulWidget extends StatefulWidget {
    MyStatefulWidget({
        Key key,
        this.parameter,
    }): super(key: key);
    
    final parameter;
    
    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

    @override
    void initState(){
        super.initState();
        
        // Additional initialization of the State
    }
    
    @override
    void didChangeDependencies(){
        super.didChangeDependencies();
        
        // Additional code
    }
    
    @override
    void dispose(){
        // Additional disposal code
        
        super.dispose();
    }
    
    @override
    Widget build(BuildContext context){
        return new ...
    }
}

下图显示了与创建有状态窗口小部件相关的操作/调用序列(简化版本)。在图的右侧,您将注意到流中的State对象的内部状态。您还将看到上下文与状态关联的时刻,从而变为可用(已安装)。

[站外图片上传中...(image-6003dd-1539427274357)]

所以让我们用一些额外的细节来解释它:

initState()

initState()方法是创建State对象后要调用的第一个方法(在构造函数之后)。

需要执行其他初始化时,将覆盖重写此方法。典型的初始化与动画,控制器有关......
如果重写此方法,则需要在第一个位置调用super.initState()方法。

在这个方法中,上下文context可用,但你还不能真正使用它,因为框架还没有完全将状态与它相关联。

initState()方法完成后,State对象现在已初始化,上下文可用。

在此State对象的生命周期内不再调用此方法。

didChangeDependencies()

didChangeDependencies()方法是要调用的第二个方法。

在此阶段,由于上下文可用,您可以使用它。

如果您的Widget链接到InheritedWidget和/或您需要初始化一些侦听器(基于BuildContext),则通常会覆盖此方法。

请注意,如果您的窗口小部件链接到InheritedWidget,则每次重建此窗口小部件时都会调用此方法。

如果重写此方法,则应首先调用super.didChangeDependencies()

build()

build(BuildContext context)方法在didChangeDependencies()(和didUpdateWidget)之后调用。

这是您构建窗口小部件(可能还有任何子树)的位置。

每次State对象更改时(或者当InheritedWidget需要通知“已注册”的小部件时)都会调用此方法!
为了强制重建,您可以调用setState((){...})方法。

dispose()

放弃窗口小部件时调用dispose()方法。

如果你需要执行一些清理(例如监听器,控制器......),然后立即调用super.dispose(),则覆盖此方法。


选择无状态还是有状态小部件?

这是许多开发人员需要问自己的问题:“我是否需要我的Widget无状态或有状态?”
为了回答这个问题,请问自己:

在我的小部件的生命周期中,我是否需要考虑一个将要更改的变量,何时更改,将强制重建小部件?

如果问题的答案是肯定的,那么您需要一个有状态的小部件,否则,您需要一个无状态小部件。
一些例子:

  • 用于显示复选框列表的小组件。要显示复选框,您需要考虑一系列项目。每个项目都是一个具有标题和状态的对象。如果单击复选框,则切换相应的item.status;在这种情况下,您需要使用有状态窗口小部件来记住项目的状态,以便能够重绘复选框。
  • 带有表格的屏幕。该屏幕允许用户填写表单的窗口小部件并将表单发送到服务器。在这种情况下,在这种情况下,除非您在提交表单之前需要验证表单或执行任何其他操作,否则无状态窗口小部件可能就足够了。

Stateful Widget由2部分组成

还记得Stateful小部件的结构吗?有两个部分:

Widget的主要定义

class MyStatefulWidget extends StatefulWidget {
    MyStatefulWidget({
        Key key,
        this.color,
    }): super(key: key);
    
    final Color color;

    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

第一部分“MyStatefulWidget”通常是Widget的公共部分。当您要将其添加到窗口小部件树时,可以实例化此部件。

此部分在Widget的生命周期内不会发生变化,但可能接受可由其相应的State实例使用的参数。

请注意,在Widget的第一部分定义的任何变量通常在其生命周期内不会更改。

Widget State定义

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
    ...
    @override
    Widget build(BuildContext context){
        ...
    }
}

第二部分“_MyStatefulWidgetState”是在Widget的生命周期中变化的部分,并强制每次应用修改时重建Widget的这个特定实例。

名称以_开头的字符使其成为.dart文件的私有。
如果需要在.dart文件之外引用此类,请不要使用“_”前缀。

_MyStatefulWidgetState类可以访问存储在MyStatefulWidget中的任何变量,使用widget.{变量的名称}
例如:widget.color

小部件唯一标识 - key

在Flutter中,每个Widget都是唯一标识的。这个唯一标识由构建/渲染时的框架定义。

此唯一标识对应于可选的Key参数。如果省略,Flutter将为您生成一个。

在某些情况下,您可能需要强制使用此密钥,以便可以通过其密钥访问窗口小部件。

为此,您可以使用以下帮助程序之一:GlobalKey <T>,LocalKey,UniqueKey 或ObjectKey。

该GlobalKey确保关键是在整个应用程序唯一的。
强制使用Widget的唯一标识:

GlobalKey myKey = new GlobalKey();
    ...
    @override
    Widget build(BuildContext context){
        return new MyWidget(
            key: myKey
        );
    }

第2部分:如何进入State?

如前所述,State链接到一个BuildContext,BuildContext链接到Widget的一个实例。

1. Widget本身

理论上,唯一能够访问状态的是Widget State本身。

在这种情况下,没有困难。Widget State类访问其任何变量。

2. 一个直接的 child Widget

有时,父窗口小部件可能需要访问其直接子节点的状态才能执行特定任务。
在这种情况下,要访问这些直接子项State,您需要了解它们。

给某人打电话的最简单方法是通过一个名字。在Flutter中,每个Widget都有一个唯一的标识,由框架在构建/渲染时确定。如前所示,您可以使用key参数强制使用Widget的标识。

...
    GlobalKey<MyStatefulWidgetState> myWidgetStateKey = new GlobalKey<MyStatefulWidgetState>();
    ...
    @override
    Widget build(BuildContext context){
        return new MyStatefulWidget(
            key: myWidgetStateKey,
            color: Colors.blue,
        );
    }

一旦确定,父Widget可以通过以下方式访问其子级的状态:

myWidgetStateKey.currentState

让我们考虑一个基本示例,当用户点击按钮时显示SnackBar。
由于SnackBar是Scaffold的子Widget,它不能直接被Scaffold身体的任何其他孩子访问(还记得上下文的概念及其层次结构/树结构吗?)。因此,访问它的唯一方法是通过ScaffoldState,它公开一个公共方法来显示SnackBar。

class _MyScreenState extends State<MyScreen> {
        /// the unique identity of the Scaffold
        final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

        @override
        Widget build(BuildContext context){
            return new Scaffold(
                key: _scaffoldKey,
                appBar: new AppBar(
                    title: new Text('My Screen'),
                ),
                body: new Center(
                    new RaiseButton(
                        child: new Text('Hit me'),
                        onPressed: (){
                            _scaffoldKey.currentState.showSnackBar(
                                new SnackBar(
                                    content: new Text('This is the Snackbar...'),
                                )
                            );
                        }
                    ),
                ),
            );
        }
    }

3. 祖先Widget

假设您有一个属于另一个Widget的子树的Widget,如下图所示。
[站外图片上传中...(image-65249f-1539427274357)]

为了实现这一目标,需要满足3个条件:

1.“带状态的小工具”(红色)需要暴露其State

为了公开它的状态,Widget需要在创建时记录它,如下所示:

class MyExposingWidget extends StatefulWidget {

   MyExposingWidgetState myState;
    
   @override
   MyExposingWidgetState createState(){
      myState = new MyExposingWidgetState();
      return myState;
   }
}

2.“Widget State”需要暴露一些getter / setter
为了让“stranger”设置/获取状态属性,Widget State需要通过以下方式授权访问:

  • 公共属性 (不推荐)
  • getter / setter

例如:

class MyExposingWidgetState extends State<MyExposingWidget>{
   Color _color;
    
   Color get color => _color;
   ...
}

3.“想要获得State的Widget”(上图中蓝色的widget)需要引用State

class MyChildWidget extends StatelessWidget {
   @override
   Widget build(BuildContext context){
      final MyExposingWidget widget = context.ancestorWidgetOfExactType(MyExposingWidget);
      final MyExposingWidgetState state = widget?.myState;
        
      return new Container(
         color: state == null ? Colors.blue : state.color,
      );
   }
}

这个解决方案很容易实现,但子窗口小部件如何知道它何时需要重建?
在这个解决方案,它不知道。它必须等待重建才能刷新其内容,这不是很方便。
下一节将讨论Inherited Widget的概念,它可以解决这个问题。


InheritedWidget

简而言之,InheritedWidget允许在窗口小部件树中有效地传播(和共享)信息。

InheritedWidget是一个特殊的Widget,您可以将其作为另一个子树的父级放在Widgets树中。该子树的所有小部件都必须能够与该InheritedWidget公开的数据进行交互。

为了解释它,让我们看下代码:

class MyInheritedWidget extends InheritedWidget {
   MyInheritedWidget({
      Key key,
      @required Widget child,
      this.data,
   }): super(key: key, child: child);
    
   final data;
    
   static MyInheritedWidget of(BuildContext context) {
      return context.inheritFromWidgetOfExactType(MyInheritedWidget);
   }

   @override
   bool updateShouldNotify(MyInheritedWidget oldWidget) => data != oldWidget.data;
}

此代码定义了一个名为“MyInheritedWidget”的Widget,旨在“共享”所有小部件(与子树的一部分)中的某些数据。

如前所述,为了能够传播/共享一些数据,需要将InheritedWidget定位在窗口小部件树的顶部,这解释了传递给InheritedWidget基础构造函数的“@required Widget child”。

“static MyInheritedWidget(BuildContext context)”方法允许所有子窗口小部件获取最接近上下文的MyInheritedWidget的实例(参见后面)

最后,“updateShouldNotify”重写方法用于告诉InheritedWidget是否必须将通知传递给所有子窗口小部件(已注册/已订阅),如果对数据应用了修改(请参阅下文)。

因此,我们需要将它放在树节点级别,如下所示:

class MyParentWidget... {
   ...
   @override
   Widget build(BuildContext context){
      return new MyInheritedWidget(
         data: counter,
         child: new Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}

子child如何访问InheritedWidget的数据?

在构建子child时,后者将获得对InheritedWidget的引用,如下所示:

class MyChildWidget... {
   ...
    
   @override
   Widget build(BuildContext context){
      final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);

      /// 从此刻开始,窗口小部件可以使用MyInheritedWidget公开的数据
      /// 通过调用:inheritedWidget.data
      return new Container(
         color: inheritedWidget.data.color,
      );
   }
}

如何在小部件之间进行交互?

请考虑以下显示窗口小部件树结构的图表。

[站外图片上传中...(image-42aa42-1539427274357)]

为了说明一种交互方式,我们假设如下:

  • '小部件A'是一个将项目添加到购物车的按钮;
  • “小部件B”是一个显示购物车中商品数量的文本;
  • “小部件C”位于小部件B旁边,是一个内部带有任何文本的文本;
  • 我们希望“Widget B”在按下“Widget A”时自动在购物车中显示正确数量的项目,但我们不希望重建“Widget C”

InheritedWidget就是用来干这个的Widget!

代码示例我们先写下代码,然后解释如下:

class Item {
   String reference;

   Item(this.reference);
}

class _MyInherited extends InheritedWidget {
  _MyInherited({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  final MyInheritedWidgetState data;

  @override
  bool updateShouldNotify(_MyInherited oldWidget) {
    return true;
  }
}

class MyInheritedWidget extends StatefulWidget {
  MyInheritedWidget({
    Key key,
    this.child,
  }): super(key: key);

  final Widget child;

  @override
  MyInheritedWidgetState createState() => new MyInheritedWidgetState();

  static MyInheritedWidgetState of(BuildContext context){
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }
}

class MyInheritedWidgetState extends State<MyInheritedWidget>{
  /// List of Items
  List<Item> _items = <Item>[];

  /// Getter (number of items)
  int get itemsCount => _items.length;

  /// Helper method to add an Item
  void addItem(String reference){
    setState((){
      _items.add(new Item(reference));
    });
  }

  @override
  Widget build(BuildContext context){
    return new _MyInherited(
      data: this,
      child: widget.child,
    );
  }
}

class MyTree extends StatefulWidget {
  @override
  _MyTreeState createState() => new _MyTreeState();
}

class _MyTreeState extends State<MyTree> {
  @override
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: new Column(
          children: <Widget>[
            new WidgetA(),
            new Container(
              child: new Row(
                children: <Widget>[
                  new Icon(Icons.shopping_cart),
                  new WidgetB(),
                  new WidgetC(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Text('${state.itemsCount}');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Text('I am Widget C');
  }
}

说明

在这个非常基本的例子中,

  • _MyInherited是一个InheritedWidget,每次我们通过点击“Widget A”按钮添加一个Item时都会重新创建它
  • MyInheritedWidget是一个Widget,其状态包含Items列表。可以通过“(BuildContext context)的静态MyInheritedWidgetState”访问此状态。
  • MyInheritedWidgetState公开一个getter(itemsCount)和一个方法(addItem),以便它们可以被小部件使用,这是子小部件树的一部分
  • 每次我们向State添加一个Item时,MyInheritedWidgetState都会重建
  • MyTree类只是构建一个小部件树,将MyInheritedWidget作为树的父级
  • WidgetA是一个简单的RaisedButton,当按下它时,从最近的MyInheritedWidget调用addItem方法
  • WidgetB是一个简单的文本,显示最接近的MyInheritedWidget级别的项目数

这一切如何运作?
注册Widget以供以后通知

当子Widget调用MyInheritedWidget.of(context)时,它会调用MyInheritedWidget的以下方法,并传递自己的BuildContext。

static MyInheritedWidgetState of(BuildContext context) {
  return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}

在内部,除了简单地返回MyInheritedWidgetState的实例之外,它还将消费者窗口小部件订阅到更改通知。

在场景后面,对这个静态方法的简单调用实际上做了两件事:

  • 当对InheritedWidget应用修改时,“consumer”窗口小部件会自动添加到将重建的订户列表中(此处为_MyInherited)
  • _MyInherited小部件(又名MyInheritedWidgetState)中引用的数据将返回给“使用者”

过程

由于'Widget A'和'Widget B'都已使用InheritedWidget订阅,因此如果对_MyInherited应用了修改,则当单击Widget A的RaisedButton时,操作流程如下(简化版本):

  • 调用MyInheritedWidgetState的addItem方法
  • MyInheritedWidgetState.addItem方法将新项添加到List <Item>
  • 调用setState()以重建MyInheritedWidget
  • 使用List <Item>的新内容创建_MyInherited的新实例
  • _MyInherited记录在参数(数据)中传递的新State作为InheritedWidget,它检查是否需要“通知”“使用者”(答案为是)
  • 它迭代整个消费者列表(这里是Widget A和Widget B)并请求他们重建
  • 由于Wiget C不是消费者,因此不会重建。

嗯,就是这么干的 !

但是,Widget A和Widget B都重建了,而重建Wiget A没用,因为它没有任何改变。如何防止这种情况发生?
在仍然访问“继承的”小组件时阻止某些小组件重建

Widget A也被重建的原因来自它访问MyInheritedWidgetState的方式。
正如我们之前看到的,调用context.inheritFromWidgetOfExactType()方法的实际上是自动将Widget订阅到“使用者”列表。

防止此自动订阅同时仍允许Widget A访问MyInheritedWidgetState的解决方案是更改MyInheritedWidget的静态方法,如下所示:

static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
    return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
                    : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }

通过添加布尔类型的额外参数...

  • 如果“rebuild”参数为true(默认情况下),我们使用常规方法(并且Widget将添加到订阅者列表中)
  • 如果“rebuild”参数为false,我们仍然可以访问数据,但不使用InheritedWidget的内部实现

因此,要完成解决方案,我们还需要稍微更新Widget A的代码,如下所示(我们添加false额外参数):

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}

在那里,当我们按下它时,Widget A不再重建。


Routes, Dialogs的特别说明......

路由Routes,对话框Dialogs , BuildContexts与应用程序绑定。这意味着即使在屏幕A内部您要求显示另一个屏幕B(例如,在当前的屏幕上),两个屏幕中的任何一个都没有“简单的方法”来关联它们自己的上下文。屏幕B了解屏幕A上下文的唯一方法是从屏幕A获取它作为Navigator.of(context).push(...。)的参数。

推荐阅读:

[1] : flutter屏幕适配
[2] : Maksim Ryzhikov
[3] : Chema Molins
[4] : Official documentation
[5] : Video from Google I/O 2018
[6] : Scoped_Model

结论

关于这些主题还有很多话要说......特别是在InheritedWidget上。
其他感兴趣的主题是Notifiers / Listeners 以及(可能主要)Streams的概念,但这将在其他文章中介绍。
感谢您阅读这篇相当长的文章,请继续关注下一个快乐的编码......

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Content: Flutter框架概况发展概述发展历史框架特性框架结构 快速入门安装Flutter在Mac OS...
    EchoZuo阅读 6,388评论 3 54
  • 上一节我们熟悉了初始化后的flutter的界面。这一节,我们就来重点了解一下这部分的内容。 StatelessWi...
    deep_sadness阅读 6,200评论 1 37
  • 写了这么多,无非就是State文档贴过来,最有感触的就两点: State 的生命周期和 StatefulWidge...
    与猎犬赛跑的野兔阅读 22,195评论 0 12
  • 醒过来,忘记昨夜多久睡去。一睁眼,就拿起手机继续昨夜的思绪。眼睛酸涩着,只有从未拉拢的窗帘缝里透出点光。 ...
    素娘阅读 207评论 0 1
  • 今天胡开浩邀请我去他家过生日后来我发现我是第一个到的。这时胡开浩刚好要去买东西。跟我说门事件让我上去。后来...
    王永吉_7fe1阅读 276评论 0 1