日志

老孟Flutter 为什么 build 方法放在 State 中而不是在 StatefulWidget 中

 来源    2021-01-13    0  

老孟导读:此篇文章是生命周期相关文章的番外篇,在查看源码的过程中发现了这一有趣的问题,欢迎大家一起探讨。

Flutter 中Stateful 组件的生命周期:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html

Flutter 中与平台相关的生命周期:http://laomengit.com/blog/20201227/%E7%9B%B8%E5%85%B3%E5%B9%B3%E5%8F%B0%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html

博客中还有更多精彩文章,也欢迎加入 Flutter 交流群。

灵活性

build 方法放在 State 中比放在 StatefulWidget 中更具灵活性,比如说,AnimatedWidgetStatefulWidget 的子类,AnimatedWidget 是一个抽象类,其中有一个 Widget build(BuildContext context) 的抽象方法,此方法需要子类重写,AnimatedWidget 源代码如下:

abstract class AnimatedWidget extends StatefulWidget {
  ...
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
  @protected
  Widget build(BuildContext context);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

  ...
}

删除了一些代码,保留了重点代码。

试想一下,如果 build 方法放在 StatefulWidget 中,则 AnimatedWidget 中的 build 方法需要带一个 State 参数,如下:

abstract class AnimatedWidget extends StatefulWidget {
  ...
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
  @protected
  Widget build(BuildContext context, AnimatedState state);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

  ...
}

但 AnimatedState 是内部实现,并不需要开放给外部(开发者),外部也不需要知道 AnimatedState 的内部实现。

闭包 this 指向异常

假设 build 方法在 StatefulWidget 中,StatefulWidget 的子类写法如下:

class MyWidget extends StatefulWidget {

  final Color color;

  @override
  Widget build(BuildContext context, MyWidgetState state) {
    print('${this.color}');
    return Container();
  }
}

此时的 this 指向的是 MyWidget 的实例,然后父组件改变颜色,重新构建 MyWidget 组件,前一个 MyWidget 的实例中的 this 依然指向前一个 MyWidget 的实例,颜色并未发生变化。

如果 build 方法在 State 中,代码如下:

class MyWidget extends StatefulWidget {
  final Color color;

  const MyWidget({Key key, this.color}) : super(key: key);
  
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    print('${widget.color}');
    return Container();
  }
}

同样,父组件改变颜色,重新构建 MyWidget 组件,此时框架更新了 State 对象的 widget 属性的引用,新的 MyWidget 实例和 $ {widget.color} 将显示绿色。

性能

有状态的组件包含StatefulWidget 和 State,当有状态组件的配置发生更改时,StatefulWidget 将会被丢弃并重建,而 State 不会重建,框架会更新 State 对象中 widget 的引用,这极大的减轻了系统重建有状态组件的工作。

此方式对动画来说极为重要,由于 State 不会被重建,保留了前面的状态,不断的根据前一个状态计算下一个状态并重建其widget,达到动画的效果。

交流

老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

相关文章
强制打字稿将方法放在实例上而不是原型上
问答是否可以强制使用typescript将方法放在实例上而不是原型上.我问这个是因为我经常遇到"这个"范围问题,原型上的方法会导致问题. 编辑 例如在ts的输出中似乎不一致,我将Foo ...
1
ios – 是否有一种方法来编译ARM而不是Xcode 4中的Thumb?
问答如果有很多浮点运算,苹果建议编译ARM而不是拇指.我的整个应用程序几乎是一个大的浮点运算. 这是他们在iOS应用程序开发工作流指南中说的: iOS devices support two instru ...
1
facebook – 如何确保方法等待http响应而不是在Dart中返回null?
问答我正在尝试在Dart中编写一个小命令行库来使用Facebook API.我有一个类'fbuser',它获取auth-token和user-id作为属性,并且有一个方法'groupIds',它应该返回一 ...
1
perl – 用Moose BUILD方法呱呱叫
问答如果BUILD方法失败,我希望我的课程爆炸.但是,如果我使用croak来处理错误,则会从Class / MOP / Method.pm而不是调用者的代码报告错误. (也就是说,实例化对象的调用者.)I ...
1
perl – 在MooseX :: Declare中创建BUILD方法的正确方法是什么?
问答我在MooseX::Declare中遇到了BUILD方法的困难.如果我说: #!/usr/bin/perl use MooseX::Declare; class Foo { has foo => ...
3
asp.net-mvc – 所有控制器都可以访问的一种方法 – 放在哪里?
问答例如,在MVC中,我有5个不同的控制器文件.在所有这些中,我有一种保存图像的方法.有没有办法将此方法放在一个地方和每个控制器方法来访问它,而不是在每个控制器中写入它? 我试过了 MyControlle ...
1
ruby-on-rails – Ruby on Rails 如何在a:属于关系中使用Active Record .build方法?
问答我一直无法找到任何关于.build方法在Rails的文档(我目前使用2.0.2). 通过实验,似乎你可以使用构建方法添加一个记录到has_many关系,然后保存任何一条记录. 例如: class Do ...
2
javascript – 在sequelize.js中使用build方法时,是否需要验证,清理或转义数据?
问答我有一个节点/快递/续集应用程序.我在sequelize中使用构建方法来创建我的foo模型的实例. Foo控制器 exports.create = function(req, res) { var f ...
1
c# – 可以将私有方法放在我的控制器中,还是应该使用asp.net mvc将它们分离成某种类型的助手类?
问答我有一个控制器根据用户类型加载一些下拉列表.例如: public ActionResult Index() { switch (SessionHelper.ViewLimit) { case &quo ...
1
android – 为什么Eclipse不能识别Notification.Builder类中的build()方法来返回Notification对象?
问答这是我的代码: NotificationManager mNotificationManager = (NotificationManager) c.getSystemService(ns); //I ...
2
javascript – 如何将方法放在Redux状态的对象上?
问答根据docs状态的反应应用程序必须是可序列化的. 那么课程呢? 假设我有一个ToDo应用程序 每个Todo项目都有属性,如名称,日期等,到目前为止这么好. 现在我想要对不可序列化的对象有方法.即Tod ...
1
ruby-on-rails – Rails 3 ActiveRecord API:.build方法
问答我是Ruby / RoR的新手(一年之外),我注意到RoR或Ruby中有几种不同的方法基本上都做同样的事情.我希望得到某种澄清的一种方法是.build方法.什么时候有效使用或如何在最好的光线下使用它, ...
2
java – 我应该把public static void main(String [] args)方法放在哪里?
问答我不认为它对程序输出有任何影响,但我应该把它放在什么类 public static void main(String[] args) { //... } 在我的程序中的方法?创建一个单独的类或将其放在 ...
1
ruby-on-rails – Rails/Ruby 1.9:有没有更好的方法将Unicode放在源文件中,而不是在每个文件的顶部粘贴#coding
问答我刚刚升级到Rails 3和Ruby 1.9.我在其中使用Unicode的所有源文件(例如emdashes)都会导致问题,直到我发现您现在需要在每个源文件的顶部包含以下魔术注释: # encoding ...
1
class-design – 为什么把私有字段和方法放在类的顶部?
问答我在很多地方在许多语言中看到这个事实上的标准,但我从来没有理解它 – 为什么你的私有字段和方法在类声明的顶部?隐喻似乎私人的东西应该位于底部(隐藏),一切公共应该在顶部,所以当你阅读通过类顶部到底部, ...
2
scala case类把方法放在伴侣对象中?
问答在case类或伴随对象中声明方法是否"更干净"(相应的更好的性能)? 例如 case class My(a:A) { def m(args) = {...} } or object ...
2
Scala把方法放在特质或者案例中?
问答有两种方法可以在Scala中为两个不同的类继承相同的特征定义一个方法. sealed trait Z { def minus: String } case class A() extends Z { ...
1
为什么我们不将C main方法放在类中?
问答在C中为什么我们不将main方法放在类中(如Java)?为什么不这样做有意义(我认为)?::我们可以. main不是保留字.但是根据语言标准,C工具链期望程序的入口点在全局范围内.因此,类中的主要内容 ...
1
objective-c – 将方法放在单独的文件中
问答我有一个类(MyClass)有很多方法.因此,.m文件变得非常难以阅读.我对Objective-C(来自REALbasic)相对较新,我想知道是否可以将MyClass中的一些方法放入不同的文件中,然后 ...
1
我是否必须将get / set方法放在matlab的类定义中?
问答是否有人强制将所有get和set函数放在Matlab的类定义文件中? 我问,因为这确实使文件有点乱,并且失败了拥有类定义文件夹的目的.::是的,如果您使用属性集和获取访问方法(实际上任何名称中带有点的 ...
1