- 原文地址:What even are Flutter widgets?
- 原文做者:Matt Carroll
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:ALVIN
- 校对者:DateBro
如下对 Flutter 组件的解释是个人我的观点,并非 Flutter 框架的组件或相关领域的官方解释。想要了解有关 Flutter 团队对其见解,请参阅 Flutter 的官方文档。前端
Flutter 是一个移动端的 UI 框架,它使 UI 开发变得更有趣、快速和简单。但从传统的 Android 和 iOS 到 Flutter,感受真的难以想象。之因此感到惊讶是由于咱们从可变的、生命周期长的 View
和 UIView
,变成了这些不可改变的、生命周期短的 Widget
。它们究竟是什么呢,它们又为何高效呢?android
最近发表了一篇关于 Widget 与 Element 和 RenderObjects 的关系的文章。我很是推荐这篇文章,建议你继续深刻研究,直到你能够彻底理解其内容为止。但对于那些已经迷失在 Widget
的人,请容许我提供一些可能有帮助的解释。ios
我一直支持在移动 UI 开发中使用视图模型。git
不管你是在 Android 仍是 iOS 上工做,都要考虑自定义的 View
或 UIView
,称为ListItemView
。这个 ListItemView
在左边显示一个图标,而后在图标右边显示字幕上方的标题,最后在右侧显示一个可选附件:github
在定义这个自定义 View
的时候,你能够将每一个对 View 的描述设为独立属性:编程
myListItemView.icon = blah;
myListItemView.title = “blah”;
myListItemView.subtitle = “blah”;
myListItemView.accessory = blah;
复制代码
从技术上来讲,这没什么问题,可是带来了架构成本。经过独立定义每一个配置,你对其描述的 Object
须要引用你的 View
,以便它能够配置每一个属性。可是,若是你使用视图模型,那么你的描述 Object
能够在不引用 View
的状况下运行,这意味着描述 Object
能够进行单元测试,而且它避免了对具体 View
的编译时依赖性:后端
class ListItem {
final Icon icon;
final String title;
final String subtitle;
final Icon accessory;
...
}
// 使用 Presenter 建立一个新的视图模型。
myListItem = myPresenter.present();
// 传递视图模型到 View 来渲染新的视图外观。
myListItemView.update(myListItem);
复制代码
这种使用视图模型的基本原理,与 Flutter 无关,但与传统的 View
相比,理解视图模型是很是重要的。视图模型是一个不可变的配置,须要应用于生命周期长的,可变的 View
。bash
传统 Android 和 iOS 中的依赖关系以下:架构
MyAndroidView -> MyAndroidViewModel
MyiOSUIView -> MyiOSViewModel
复制代码
换句话说,在传统的 Android 和 iOS 中,咱们主要使用可变的,生命周期长的 View
(和 UIView
)。咱们经过使用那些生命周期长的 View
(和UIView
)Object
来定义布局 XML,Storyboard 和可编程的布局。而后,咱们不按期会传递新的视图模型来改变它们的界面。框架
如今,让咱们来谈谈 Flutter。
与其使用可变的、生命周期长,且会不按期接收新的视图模型的 View
,不如咱们只使用不可变视图模型,来配置可变的、生命周期长的 View
?
之前是:
MyView -> MyViewModel
复制代码
如今改成:
MyViewModel -> MyView
复制代码
就像这样,简单来讲,咱们刚刚发明了 Flutter 的组件系统:
MyWidget -> MyElement
MyWidget -> MyRenderObject
复制代码
这些组件都是不可变的,其中包含了许多用于配置渲染内容方式的属性:
// 这个组件看起来确定很像一个视图模型,不是吗?
new Container(
width: 50.0,
height: 50.0,
padding: EdgeInsets.all(16.0),
color: Colors.black,
);
// Flutter 组件和传统视图有一个很大的区别
// 就是这些组件一样能够
// 实例化生命周期长的视图
final mutableSubtree = myContainer.createElement();
final mutableRender = myContainer.createRenderObject();
复制代码
但为何这些组件能建立这两样东西呢?我认为,组件应该只能建立一个生命周期长的视图?
在 Flutter 中,父/子的概念独立于渲染而存在。在 iOS 和 Android 中,父/子关系与绘制到屏幕的概念是一致的。
例如,在 Android 中,ViewGroup 须要负责:
// 父/子关系
myViewGroup.addView(...);
myViewGroup.removeView(...);
// 以及
// 布局和绘制
myViewGroup.measure(...);
myViewGroup.layout(...);
myViewGroup.draw(...);
复制代码
在 Flutter,咱们有
// Element 来管理父/子关系
myElement.mount(); // 这建立并添加子级组件
myElement.forgetChild(...); // 移除子级组件
// 用 RenderObjects 来布局和绘制:
myRenderObject.layout();
myRenderObject.paint();
复制代码
因此说,尽管 Flutter 中的组件负责建立一个 Element
和一个 RenderObject
,但这两个 Object
的组合等同于 Android 单个 ViewGroup
相同的功能。
所以,在 Flutter 中,咱们使用能够配置 View
的视图模型,而不是使用视图模型配置的 View
。这种关系是颠倒的。
颠倒视图模型的关系,以及若是一个视图模型知道如何实例化相对应的一个长生命周期的视图,你可能会感到特别奇怪。但 Flutter 向咱们展现的是,经过颠倒这种关系,咱们实现了以声明方式组合用户界面的能力。
在我看来,Flutter 正在作的事情,从根本上说,像是正在接近绘制像素的特定领域语言。
特定领域的语言是几乎全部开发人员的终极目标。若是你正在为航空业开发应用,那么你将花费大量时间构建行业特定术语的实现,如航班清单、登机牌、座位分配和会员身份。你在利用较低级别的语言语义,来表示这些术语在你的行业中的含义。然而,理想状况下,在某些时候,开发人员将中止使用这种较低级别的构造方式,他们将开始使用像 FlightManifest
、BoardingPass
和 SeatAssignment
这样的 Object
来实现整个应用。
但并不是每一个问题都是商业问题。一些问题是技术问题,例如渲染。渲染用户界面自己就是一个问题范畴。Flutter 正经过设计出用于渲染用户界面的一种特定领域的语言来解决此问题。就像 SQL 是用于搜索信息声明式的领域特定语言同样,Flutter 的组件系统正在成为用于组合用户界面的声明式的领域特定语言。这能够经过在外部放置不可变视图模型,同时将可变视图限制在内部来实现。
但愿这个视角,能够帮助你理解和欣赏 Flutter 中的组件。可是若是没有,只要你继续使用 Flutter 的 API,最终你也会体验出个中的妙处。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。