热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Joey的Flutter之旅(2)Widget基本概念

前面的文章我们对一个简单FlutterDemo进行了大篇幅的分析,整体上我们对Flutter的一些特性已经有所了解,但是细节上我们并没有过多展开。从现在开始,我们从Flutter开

前面的文章我们对一个简单 Flutter Demo 进行了大篇幅的分析,整体上我们对 Flutter 的一些特性已经有所了解,但是细节上我们并没有过多展开。从现在开始,我们从 Flutter 开发框架的一系列基本概念入手,逐步构建起 Flutter 应用开发完整的知识体系。
本文要讨论的是 Flutter 中一个最基本最重要的概念 —— Widget(部件)。(关于具体的 Widget 应用会在以后的开发讲解文章中涉及,本文先从基础概念开始)

一、何为 Flutter Widget?

先上定义

我们看到官方文档中给出 Widget 的解释为:

“Widgets
describe what their
view should look like given their current configuration and state.”

怎么理解呢,Flutter 中,Widget 是对视图的一种包含配置及状态信息的“描述数据”,用于约束具体的视图元素。 也就是说,Widget 是一种描述,他是渲染绘制 UI 的依据,而不是具体的 View。

很多人就纳闷了,视图就视图吧,为啥要设计一个描述数据拐个弯路,直接做成 View 不就行了吗?不着急,往下看。

响应式编程范式

要理解 Widget 的设计思想,我们首先要明确 Flutter 的设计思想,官方对 Flutter 的解释是,Flutter 是受 React 编程范式启发而设计的“现代响应式框架”,问题又来了,那么什么是“响应式”呢?

“响应式”按照标准定义的话,是一种基于“数据流”模型的声明式编程范式。所谓“声明式”,是相对于“命令式”的,我们最早接触的编程都是命令式的,同一个流程,“命令式”会将命令下达到每一个具体的步骤,直到达到目标,而“声明式”,直接声明预期的结果,程序自己完成中间的步骤,也就是说“声明式”编程范式的关键是让程序能理解你的描述,知道干什么,然后自发的去完成。

听上去很复杂?其实不然,真的不是什么很高大上的东西,只是一种思想/范式而已,你其实早就已经接触过了。比如你经常制作的 Excel 表格:

《Joey的Flutter之旅 - (2) Widget 基本概念》
《Joey的Flutter之旅 - (2) Widget 基本概念》

这是一个简单的统计表格,注意,有个关键的地方,“当月总数”与“总数量”都是通过 Excel 公式定义自动计算的,当我们填充好 ABCD 各月数据之后,求和结果都已经出来了,你没有直接去操作这些总和,对吧,接着看:

《Joey的Flutter之旅 - (2) Widget 基本概念》
《Joey的Flutter之旅 - (2) Widget 基本概念》

当你将 B 的 2 月数据调整为 30 的时候,数值提升造成的影响首先传递到了“当月总数”,致其增加,然后这一增加的影响又传递到了“总数量”,引起了一连串的基于数据流的变化,而这些变化是自发的,所以再重复一遍,自发的前提是因为响应式知道如何响应,比如我们事先给出的公式,所以我们仅仅给出 B 的数据描述,表格就能自己完成剩下的工作,这就是典型的响应式思想。

试想一下,如果采用命令式的编程方式,步骤应该是:主动增加 B 数值,主动增加“当月总数”,主动增加“总数量”,结束,emmmmm…而相比之下,响应式的步骤则是:修改 B 到目标数值,结束。所以,大家心里就有个数了,为什么现在响应式编程范式这么流行。

再论 Widget

Widget 就是基于这种响应式思想而设计出来的,我们是希望视图等一些数据通过读取 Widget 的描述信息来进行响应变化,无论是一些配置信息还是状态的变化信息,而不是直接去操作视图。即 Flutter 中的 Widget 并不能简单的理解为一种 View,比如我们知道 Android 等 Native 开发中的 View 是可以由程序员直接操作的,经常有各种 .setXXXX(XXXX) 的操作,这是命令式的方式,不符合响应式的思想,所以 View 与 Flutter 的 Widget 便不是一个概念。

二、关于 Widget,我们要了解的几点

Flutter 中 Widget 是不可变

Flutter 中,Widget 被设计为“immutable”,类似于 Java 中的 String,你无法去修改一个已经创建的 immutable 对象。如果想去改变一个 Widget,唯一的方式是去重建他,没错,Flutter 也是这么做的。当 Widget 维持的状态发生改变时,通过State.setState,使得相关联的 Widget 发生重建,这将作为视图更新的依据。

有人认为频繁的重建 Widget 不是什么好事情,其实首先来说,Widget 只是视图的一种描述数据,重建的过程并不直接引起视图重绘等操作,他是很轻量级的,重建并不会有太大的性能开销,另一方面,Dart 针对 Widget 经常重建这一点对内存管理做了很大的优化,采用了分代无锁 GC ,将 GC 对性能的影响降至最低,所以在这一方面不必纠结过多。

Everything is Widget

Flutter 的设计思想是“Everything is Widget”,在 Flutter 中,Widget 是一个比较宽泛的概念,无论基本部件、布局、还是手势等都是 Widget。举个例子,一般的原生开发中我们会认为 center 是的一种布局属性,而 Flutter 中,center 也设计一种 Widget,我们既然理解了 Widget 是对视图的一种“描述数据”,那么就很容易理解 center 这种 Widget 的意义,center 可以理解为“内部的视图元素居中绘制”,就是这么一段描述而已,会成为视图绘制的一种约束。

《Joey的Flutter之旅 - (2) Widget 基本概念》
《Joey的Flutter之旅 - (2) Widget 基本概念》

Widget 有点类似于 Android 中 XML 的角色,对视图做出了约束,但他的职责比 XML 要多,因为有一些 Widget 并不是和特定的 View 对应的,如一些手势 Widget 等。还是那句话,Flutter 中,Everything is Widget,所以需要在以后的开发中慢慢领会。

Widget 实现“多孩子”

一个普通的 Widget 只能有一个 Child,如果想要有多个孩子,应该用这种方式去实现:在 将Row, Column, 或者Stack作为 Widget 的 Child 进行嵌套,这三个布局 Widget 有 Children 属性,意味着可以有多个孩子。

三、再论 StatelessWidget 与 StatefulWidget

Flutter 开发中,你大部分时间都是在与这两种 Widget 打交道,通过上一篇文章对于 StatelessWidget 与 StatefulWidget 用法的简单认识以及上文中对于 Widget 的讨论,现在回过头来看 StatelessWidget 与 StatefulWidget 就容易理解了。

StatefulWidget 相比 StatelessWidget 多了一个对于状态的维持。也就是说,对于需要维持状态信息的 Widget,我们要采用 StatefulWidget,我们在程序中通过对 state 进行更新,通过State.setState使得 Widget 重建,最终在视图中响应状态的变化;对于不需要单独维持状态的 Widget,我们就要采用 StatelessWidget。

Flutter 鼓励我们能用 StatelessWidget 就去用 StatelessWidget,我们应该将真正需要携带 state 的 Widget 设计成 Statefulidget,其它的越简单越好。

理解 widget 与 state 的生命周期

我们要明白的是,尽管一些 StatefulWidget 与 state 紧紧关联,但是 state 的生命周期与 Widget 的生命周期是不一致的,state 可以持久的存在,需要的时候自身作出更改即可,而许多 Widget 都比较短命,伴随着 state 的更改,Widget 要经历不停的销毁和重建。

四、Widget 哲学 — 组合大于继承

Flutter 中,很多 Widget 本身通常由许多小型、单用途的 Widget 组成,结合起来产生强大的效果,类的层次结构是扁平的,以最大化可能的组合数量,这是一种非常灵活的思想,我认为每一位 Flutter 开发者都应该将这种思想深刻领会。 Widget 是相当灵活的,灵活就会带来挑战,我认为不仅仅是对于一些基本部件的封装遵循“组合大于继承”,在实际的业务开发中也应该时刻注意拿捏项目中各个功能 Widget 的职责分离与封装粒度,而且这个问题将会在项目变得庞大起来后显得更为重要。

以之前的计数器 Demo 为例,点击浮动按钮产生计数变化仅仅用了一个 MyHomePage 这个 StatefulWidget:

class MyHomePage extends StatefulWidget {...}
class _MyHomePageState extends State<MyHomePage> {
// Text 用来显示计数器数值 // floatingActionButton 用来触发计数增加事件修改 state }

这个 Demo 实现一个增加计数的计数器功能是没有任何问题的,但是如果我们想复用其中的“数值显示部件”或者“增加计数按钮”就很困难了,如果换一种方式去封装 Widget:

// 数值显示 Widget class CounterDisplay extends StatelessWidget {...}
// 计数增加 Widget class CounterIncrementor extends StatelessWidget {...}
// 计数数值 Widget class Counter extends StatefulWidget {...}
class _CounterState extends State<Counter> {
...
// 调用封装好的 wiget CounterIncrementor(onPressed: _increment),
CounterDisplay(count: _counter),
...
}

这样抽离出来各个功能的 Widget 是不是更符合“职责分离”的原则,有利于各部分 Widget 的复用,也避免了某些 Widget 过于臃肿难以维护的情况。这毕竟只是个简单的 Demo,试想如果一个复杂的 UI 页面,将诸多 Widget 不加分离直接写在一起的话,恐怕连阅读代码都很困难。

总结

Widget 是 Flutter 中最基本也是最重要的概念,我们在今后的开发过程中要时时刻刻和他打交道。所以,我们对于 Widget 的学习,不能仅仅停留在会应用的层面,而是应该从 Flutter 的设计思想,Widget 的设计背景上深入地去了解,再结合平时使用 Widget 的一些经验,一些疑问,不断地深化对 Widget 这一概念的认识。万丈高楼平地起,我相信基础打好了,这些最基本的概念理解透彻了,以后面对一些复杂的项目才能更加得心应手。

参考资料

  1. Introduction to widgets
  2. 闲鱼技术:深入了解Flutter界面开发(强烈推荐)
  3. 闲鱼技术:Flutter快速上车之Widget

推荐阅读
  • 经过半年的精心整理,我们汇总了当前市场上最全面的Android面试题解析,为移动开发人员的晋升和加薪提供了宝贵的参考资料。本书详细涵盖了从基础到高级的各类面试题,帮助读者全面提升技术实力和面试表现。章节目录包括:- 第一章:Android基础面试题- 第二章:... ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
  • 在机器学习领域,深入探讨了概率论与数理统计的基础知识,特别是这些理论在数据挖掘中的应用。文章重点分析了偏差(Bias)与方差(Variance)之间的平衡问题,强调了方差反映了不同训练模型之间的差异,例如在K折交叉验证中,不同模型之间的性能差异显著。此外,还讨论了如何通过优化模型选择和参数调整来有效控制这一平衡,以提高模型的泛化能力。 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 本文源自极分享,详细内容请参阅原文。技术债务如同信用卡负债,随着时间推移,修复成本会越来越高,因此程序员必须对此有深刻认识。此外,团队应致力于培养一种持续维护和优化代码的文化,以减少技术债务的累积。 ... [详细]
  • 非计算机专业的朋友如何拿下多个Offer
    大家好,我是归辰。秋招结束后,我已顺利入职,并应公子龙的邀请,分享一些秋招面试的心得体会,希望能帮助到学弟学妹们,让他们在未来的面试中更加顺利。 ... [详细]
  • 本文将带你快速了解 SpringMVC 框架的基本使用方法,通过实现一个简单的 Controller 并在浏览器中访问,展示 SpringMVC 的强大与简便。 ... [详细]
  • 双指针法在链表问题中应用广泛,能够高效解决多种经典问题,如合并两个有序链表、合并多个有序链表、查找倒数第k个节点等。本文将详细介绍这些应用场景及其解决方案。 ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
  • HBase Java API 进阶:过滤器详解与应用实例
    本文详细探讨了HBase 1.2.6版本中Java API的高级应用,重点介绍了过滤器的使用方法和实际案例。首先,文章对几种常见的HBase过滤器进行了概述,包括列前缀过滤器(ColumnPrefixFilter)和时间戳过滤器(TimestampsFilter)。此外,还详细讲解了分页过滤器(PageFilter)的实现原理及其在大数据查询中的应用场景。通过具体的代码示例,读者可以更好地理解和掌握这些过滤器的使用技巧,从而提高数据处理的效率和灵活性。 ... [详细]
  • 为了评估精心优化的模型与策略在实际环境中的表现,Google对其实验框架进行了全面升级,旨在实现更高效、更精准和更快速的在线测试。新的框架支持更多的实验场景,提供更好的数据洞察,并显著缩短了实验周期,从而加速产品迭代和优化过程。 ... [详细]
  • 本文详细解析了LeetCode第215题,即高效寻找数组中前K个最大元素的问题。通过使用快速选择算法(partition),可以在平均时间复杂度为O(N)的情况下完成任务。本文不仅提供了算法的具体实现步骤,还深入探讨了partition算法的工作原理及其在不同场景下的应用,帮助读者更好地理解和掌握这一高效算法。 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
author-avatar
肉斯情-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有