问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

FlutterWidget原理解读(一)

发布网友 发布时间:2024-09-28 12:05

我来回答

1个回答

热心网友 时间:2024-09-30 20:20

前言

使用过Flutter的同学,应该都听过一句话“everythingisawidget——在Flutter中万物皆是Widget”。

虽然不能说在Flutter开发中所有代码模块都是一个Widget,但足以说明Widget在Flutter中的重要性,本篇文章就重点关于FlutterWidget的原理进行解读。

Widget简介

什么是Widget?我们先看一下官方的描述

“==Describestheconfigurationforan[Element]==”

在Flutter中,Widget的功能是“描述一个UI元素的配置数据”。

这句话很简单,如何理解呢?暂时可以简单的理解,FLutter最终绘制在设备上的显示元素,都是通过Widget配置出来的。

在web前端开发中,我们知道浏览器页面由HTML+CSS+JS配置而成,其中HTML负责配置UI结构,CSS负责配置UI样式,JS负责UI的交互。

而在Flutter中,无论是UI结构,还是UI样式,再到UI交互都是通过Widget完成。例如:

Widget树结构配置UI结构

样式Widget,Padding、Color等

交互Widget,GestureDetector等

Widget分类

在Flutter中,官方提供的原生Widget多达300+,这么多Widget,在基础原理层面是如何分类的呢?

使用过Flutter的同学,最熟悉的应该是StatelessWidget和StatefulWidget两种Widget,除了这两种还要其他的吗?

我们来看一下FlutterWidget组件继承图。

从上图中,我们知道继承Widget基类四个子类分别是

StatelessWidget

StatefulWidget

RenderObjectWidget

ProxyWidget

其中前三类StatelessWidget、StatefulWidget、RenderObjectWidget负责UI渲染配置,而ProxyWidget继承的子类InheritedWidget负责Widget树向下传递数据。

如果按照功能来分类,则可分成两大类:

UI渲染配置Widget:StatelessWidget、StatefulWidget、RenderObjectWidget

UI树数据状态管理Widget:InheritedWidget

StatelessWidget、StatefulWidget、RenderObjectWidget又可依据UI配置类型Widget,分成两类:

组合Widget:StatelessWidget、StatefulWidget

自定义渲染Widget:RenderObjectWidget

接下来,本篇文章主要讲解UI配置类型Widget,UI树数据状态管理Widget——InheritedWidget,将在下一篇文章中讲解。

组合Widget自定义渲染Widget区别?

在日常业务开发中,开发者只需要使用组合Widget就能满足99%的业务功能,所以对于初学Flutter的同学来说,学会StatelessWidget与StatefulWidget的使用就能满足业务开发需求。

组合Widget与自定义渲染Widget有什么区别呢?

站在前端的角度,我们开发一个HTML页面,只需要使用W3C定义的标准的div、span等标签和css样式position、color等即可搭建一个完整的页面。

至于div、color浏览器最终是如何渲染的,无需开发者定义实现,全权由浏览器引擎原生实现。开发者基于div+css开发的组件都属于组合组件,等同于组合Widget。

那什么是自定义渲染Widget呢?就好比,浏览器未支持css3之前,如果要实现边框圆角样式“border-radius”使用css是做不到的。假如浏览器提供前端开发者自定义css样式渲染的接口,由前端开发者实现边框圆角的css渲染,则属于自定义渲染组件,等同于与自定义渲染Widget。

组合Widget,StatelessWidget与StatefulWidget

我们先看看,源码抽象类的定义

StatelessWidget源码

abstractclassStatelessWidgetextendsWidget{constStatelessWidget({Key?key}):super(key:key);@overrideStatelessElementcreateElement()=>StatelessElement(this);@protectedWidgetbuild(BuildContextcontext);}

StatefulWidget

abstractclassStatefulWidgetextendsWidget{constStatefulWidget({Key?key}):super(key:key);@overrideStatefulElementcreateElement()=>StatefulElement(this);@protected@factoryStatecreateState();//ignore:}

从源代码我们可以看出,StatelessWidget是一个无状态组件,提供一个组件构建函数build。StatefulWidget是一个有状态组件,提供一个状态创建函数createState。

接下来看看StatefulWidget类中依赖State类的源码

abstractclassState<TextendsStatefulWidget>withDiagnosticable{Tgetwidget=>_widget!;T?_widget;BuildContextgetcontext{assert((){if(_element==null){throwFlutterError('Thiswidgethasbeenunmounted,sotheStatenolongerhasacontext(andshouldbeconsidereddefunct).\n''Considercancelinganyactiveworkring"dispose"orusingthe"mounted"gettertodetermineiftheStateisstillactive.',);}returntrue;}());return_element!;}StatefulElement?_element;boolgetmounted=>_element!=null;@protected@mustCallSupervoidinitState(){}@mustCallSuper@protectedvoiddidUpdateWidget(covariantToldWidget){}@protected@mustCallSupervoidreassemble(){}@protectedvoidsetState(VoidCallbackfn){_element!.markNeedsBuild();}@protected@mustCallSupervoiddeactivate(){}@protected@mustCallSupervoidactivate(){}@protected@mustCallSupervoiddispose(){}

从上面代码中可以看出,State是一个有状态的组件,有生命周期钩子函数initState、dispose等和状态改变函数setState。

@protectedvoidsetState(VoidCallbackfn){_element!.markNeedsBuild();}

从从setState源码定义可以知道,setState会触发组件重渲染函数markNeedsBuild。

从源码对比来看StatelessWidget实现非常简单,连组件生命周期的钩子函数都没有,而StatefullWidget则相对复杂许多。

有不少生命周期钩子函数

有状态存储对象

有修改状态对象的函数setState

如果用React组件类比,则StatelessWidget相当于纯函数组件,而StatefullWidget则是类组件。

且StatelessWidget和StatefullWidget使用场景也跟React纯函数组件和类组件使用场景相同,在此不做赘述。

StatefulWidget生命周期流程图

重点关注如下生命周期钩子:

initState():widget第一次插入widget树调用,此时还没有触发build函数,且整个state生命周期只调用一次

didUpdateWidget():当State对象的状态发生变化时,重新build之前调用,一般在这里判断哪些状态变化是需要触发哪些业务函数时调用。

dispose():当State对象从树中被永久移除时调用,通常在此回调中释放资源。

自定义渲染Widget——RenderObjectWidget

我们先看看RenderObjectWidget子类继承关系图。

从上图可以得知RenderObjectWidget分成三类:

LeafRenderObjectWidget

SingleChildRenderObjectWidget

MultiChildRenderObjectWidget

Flutter原生基础布局组件都是通过继承SingleChildRenderObjectWidget或MultiChildRenderObjectWidget实现。

接下来我们看看源码实现:

RenderObjectWidget

abstractclassRenderObjectWidgetextendsWidget{provideconstRenderObjectWidget({Key?key}):super(key:key);@override@factoryRenderObjectElementcreateElement();@protected@factoryRenderObjectcreateRenderObject(BuildContextcontext);@protectedvoipdateRenderObject(BuildContextcontext,covariantRenderObjectrenderObject){}@protectedvoiddidUnmountRenderObject(covariantRenderObjectrenderObject){}}

LeafRenderObjectWidget

abstractclassLeafRenderObjectWidgetextendsRenderObjectWidget{provideconstLeafRenderObjectWidget({Key?key}):super(key:key);@overrideLeafRenderObjectElementcreateElement()=>LeafRenderObjectElement(this);}

SingleChildRenderObjectWidget

abstractclassSingleChildRenderObjectWidgetextendsRenderObjectWidget{provideconstSingleChildRenderObjectWidget({Key?key,this.child}):super(key:key);finalWidget?child;@overrideSingleChildRenderObjectElementcreateElement()=>SingleChildRenderObjectElement(this);}

MultiChildRenderObjectWidget

abstractclassMultiChildRenderObjectWidgetextendsRenderObjectWidget{MultiChildRenderObjectWidget({Key?key,this.children=const<Widget>[]})}finalList<Widget>children;@overrideMultiChildRenderObjectElementcreateElement()=>MultiChildRenderObjectElement(this);}

从源码可以看出LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget处理RenderObjectWidget个数有差异。

SingleChildRenderObjectWidget:处理单个RenderObjectWidget。

MultiChildRenderObjectWidget:处理多个RenderObjectWidget。

LeafRenderObjectWidget:叶子渲染Widget,处理没有children的RenderObjectWidget。

而继承RenderObjectWidget的自定义子类最重要是需要实现抽象函数createRenderObject、updateRenderObject,对应创建、更新

拿Padding原生Widget源码实现距离。

classPaddingextendsSingleChildRenderObjectWidget{///Createsawidgetthatinsetsitschild.//////The[padding]argumentmustnotbenull.constPadding({Key?key,requiredthis.padding,Widget?child,}):assert(padding!=null),super(key:key,child:child);///Theamountofspacebywhichtoinsetthechild.finalEdgeInsetsGeometrypadding;@overrideRenderPaddingcreateRenderObject(BuildContextcontext){returnRenderPadding(padding:padding,textDirection:Directionality.maybeOf(context),);}@overridevoipdateRenderObject(BuildContextcontext,RenderPaddingrenderObject){renderObject..padding=padding..textDirection=Directionality.maybeOf(context);}@overridevoiddebugFillProperties(DiagnosticPropertiesBuilderproperties){super.debugFillProperties(properties);properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding',padding));}}

从源码实现来看,传入PaddingWidget的子Widget直接传递到父SingleChildRenderObjectWidgetchild,而Padding只是实现Widget容器布局RenderPaddingcreateRenderObject(BuildContextcontext),具体实现需要看RenderPadding实现源码,如下:

classRenderPaddingextendsRenderShiftedBox{///Createsarenderobjectthatinsetsitschild.//////The[padding]argumentmustnotbenullandmusthavenon-negativeinsets.RenderPadding({requiredEdgeInsetsGeometrypadding,TextDirection?textDirection,RenderBox?child,}):assert(padding!=null),assert(padding.isNonNegative),_textDirection=textDirection,_padding=padding,super(child);EdgeInsets?_resolvedPadding;void_resolve(){if(_resolvedPadding!=null)return;_resolvedPadding=padding.resolve(textDirection);assert(_resolvedPadding!.isNonNegative);}void_markNeedResolution(){_resolvedPadding=null;markNeedsLayout();}///Theamounttopadthechildineachdimension.//////Ifthisissettoan[EdgeInsetsDirectional]object,then[textDirection]///mustnotbenull.EdgeInsetsGeometrygetpadding=>_padding;EdgeInsetsGeometry_padding;setpadding(EdgeInsetsGeometryvalue){assert(value!=null);assert(value.isNonNegative);if(_padding==value)return;_padding=value;_markNeedResolution();}///Thetextdirectionwithwhichtoresolve[padding].//////Thismaybechangedtonull,butonlyafterthe[padding]hasbeenchanged///toavaluethatdoesnotdependonthedirection.TextDirection?gettextDirection=>_textDirection;TextDirection?_textDirection;settextDirection(TextDirection?value){if(_textDirection==value)return;_textDirection=value;_markNeedResolution();}@overridedoublecomputeMinIntrinsicWidth(doubleheight){_resolve();finaldoubletotalHorizontalPadding=_resolvedPadding!.left+_resolvedPadding!.right;finaldoubletotalVerticalPadding=_resolvedPadding!.top+_resolvedPadding!.bottom;if(child!=null)//nextlinereliesondouble.infinityabsorptionreturnchild!.getMinIntrinsicWidth(math.max(0.0,height-totalVerticalPadding))+totalHorizontalPadding;returntotalHorizontalPadding;}@overridedoublecomputeMaxIntrinsicWidth(doubleheight){_resolve();finaldoubletotalHorizontalPadding=_resolvedPadding!.left+_resolvedPadding!.right;finaldoubletotalVerticalPadding=_resolvedPadding!.top+_resolvedPadding!.bottom;if(child!=null)//nextlinereliesondouble.infinityabsorptionreturnchild!.getMaxIntrinsicWidth(math.max(0.0,height-totalVerticalPadding))+totalHorizontalPadding;returntotalHorizontalPadding;}@overridedoublecomputeMinIntrinsicHeight(doublewidth){_resolve();finaldoubletotalHorizontalPadding=_resolvedPadding!.left+_resolvedPadding!.right;finaldoubletotalVerticalPadding=_resolvedPadding!.top+_resolvedPadding!.bottom;if(child!=null)//nextlinereliesondouble.infinityabsorptionreturnchild!.getMinIntrinsicHeight(math.max(0.0,width-totalHorizontalPadding))+totalVerticalPadding;returntotalVerticalPadding;}@overridedoublecomputeMaxIntrinsicHeight(doublewidth){_resolve();finaldoubletotalHorizontalPadding=_resolvedPadding!.left+_resolvedPadding!.right;finaldoubletotalVerticalPadding=_resolvedPadding!.top+_resolvedPadding!.bottom;if(child!=null)//nextlinereliesondouble.infinityabsorptionreturnchild!.getMaxIntrinsicHeight(math.max(0.0,width-totalHorizontalPadding))+totalVerticalP
FlutterWidget原理解读(一)

在Flutter中,Widget的功能是“描述一个UI元素的配置数据”。 这句话很简单,如何理解呢?暂时可以简单的理解,FLutter最终绘制在设备上的显示元素,都是通过Widget配置出来的。 在web前端开发中,我们知道浏览器页面由HTML+CSS+JS配置而成,其中HTML负责配置UI结构,CSS负责配置UI样式,JS负责UI的交互。 而在Flutter中,无论...

ZESTRON电子失效分析

电子失效分析是指对电子元件或系统进行系统调查,以确定失效原因。通过显微镜、光谱学和电气测试等技术,分析人员可以查明导致故障的缺陷或问题。此过程包括检查物理损坏、分析电气特性和进行环境测试以确定根本原因。电子故障分析在半导体制造...

flutter 状态管理 InheritedWidget 原理分析

InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!这个特性在一些需要在widget树中共享数据的场景中非常方便!如Flutter SDK中正是通过InheritedWidget...

万字长文!一文搞懂 Flutter 局部刷新机制

首先,InheritedWidget 与 StatefulWidget 在继承结构上有所不同,InheritedWidget 通过 ProxyWidget 继承,最终成为 Widget 的子类,而 StatefulWidget 直接继承自 Widget。在创建渲染元素时,InheritedWidget 返回的是 InheritedElement,而 StatefulWidget 则返回 StatefulElement。这一差异在渲染控制中起着关键作用。...

Flutter面试:渲染原理

页面中的各界面元素(Widget)以树的形式组织,即控件树。Flutter通过控件树中的每个控件创建不同类型的渲染对象,组成渲染对象树。而渲染对象树在Flutter的展示过程分为三个阶段:布局、绘制、合成和渲染。(一)布局 Flutter采用深度优先机制遍历渲染对象树,决定渲染对象树中各渲染对象在屏幕上的位置和尺寸...

Flutter(三)之Flutter的基础Widget

一.文本Widget 在Android中,我们使用TextView,iOS中我们使用UILabel来显示文本; Flutter中,我们使用Text组件控制文本如何展示;1.1.普通文本展示 在Flutter中,我们可以将文本的控制显示分成两类: 控制文本布局的参数:如文本对齐方式textAlign、文本排版方向textDirection,文本显示最大行数maxLines、文本截断规则overflow等等,...

Widget总结

Flutter中Widget,State和BuildContext的概念是每个Flutter开发人员需要完全理解的最重要概念之一。这里先讲解一下Widget以及Widget。三者之间的关系会在最后一篇总结一下。Widget类在Flutter中是非常重要的,继承自Widget类的有PreferredSizeWidget、ProxyWidget、RenderObjectWidget、StatefulWidget、StatelessWidget。我们...

Flutter:深度解析Inherited局部刷新原理

=newWidget Inherited局部刷新机制使用过InheritedWidget的同学都会发现:对InheritedWidget节点进行setState操作,它的子组件中只有依赖于状态的子组件重走了build方法,其余无依赖关系的子组件没有重新build。1.干掉全量刷新从源码分析,InheritedElement继承自ProxyElement,ProxyElement继承自ComponentElement。将...

Flutter框架的简单理解

Flutter 的 widget 层面设计简洁,通过组合更小的、单一用途的 widget 来创建复杂的布局和效果。容器 widget 是一个常见的例子,它由多个 widget 组合而成,负责布局、绘制、定位和调整大小。具体而言,容器 widget 由 LimitedBox、ConstrainedBox、Align、Padding、DecoratedBox 和 Transform 组成。开发者可以...

Flutter文字渲染模块总结(一)

Skia,Flutter的图形库,通过智能的缓存机制优化了字形解析和渲染过程。路径缓存和Mask技术,让文字渲染如丝般流畅。每一步,从metrics信息的获取到文字位置的确定,都经过精心设计,旨在提供最佳视觉效果。RichText与EditableText:核心组件的魔法 RichText和EditableText是文字渲染的两大核心,它们分别为富文本...

用flutter实现富文本编辑器(一)

实现移动端富文本编辑器的最佳方案中,Flutter成为了当前的理想选择。对比原生开发与Web技术,原生开发的成本高昂,而Web技术虽能提供良好的用户体验,却受限于平台兼容性。然而,尽管Flutter具备诸多优点,其对富文本的支持似乎仍有所欠缺。深入了解后,我们可以看到,Flutter中与文本相关的三个Widget——Text...

原理解读 文本解读的基本原理 金字塔原理解读 中医原理精髓解读 自然哲学的数学原理解读 消防原理解读 宪法原理解读 金字塔原理解读及运用 可以原理
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
乾隆的夜壶是什么做的?拜托各位大神 云便签如何设置在桌面显示便签内容 云便签怎么设置在桌面显示便签内容... 手机root是什么意思中文翻译(手机root是什么意思) 入手了韩版k版,什么都还好.究竟能不能root vivo v3ma究竟怎么才能root? 把所有的root软件都用了一遍,全部root失败... 金价什么时候会涨 是vivox5 max的八核好还是vivoxshot的四核好 vivox5和vivoxshot哪个性比价高 vivoxshot和x5哪个配置高哪个内存大? vivo手机otg连接上没反应?? xUtils发送get请求,一直报错,Target host must not be null,or... Argument 'Key must not be null' cannot be null. 参数名: Key must... jdg线管是什么意思? 8月7号情人节很急问题..~!!! 丫0u'd better的音标 苏教版六年级下册英语where d|d 丫0u g0的翻译 车子自燃保险公司赔偿吗 ...不管在什么情况下,你会有太多表达出来的情绪麽? 扫地机器人有必要买吗,扫地机器人到底实不实用_扫地机器人有用吗 咸阳汽车北站发往旬邑的车明天发不? 梦见一直跑是什么意思? 乐华电视好在哪里? 乐华液晶电视怎么样 乐华电视是品牌吗 涂隔离霜前要洗干净脸吗? 鸡蛋汉堡的家常做法有哪些? ...几部鬼片看看?不要血腥的,恐怖的可以,最好有悬疑。。。谢谢啦... 近几年有什么好看的剧情悬疑电影啊,欧美的,谢谢啦 谁能给我介绍几个好看的恐怖电影,不要太血腥的哦,谢谢啦! ...好看的恐怖片,动作或者飙车类型的电影,要有剧情的,最好是美产的... 科目二考试通过只要签字了就可以了吗 Spring系列之JDBC对不同数据库异常如何抽象的? 怎么把抖音里一张图片翻成一张的效果 美图秀秀如何制作文字图片? vivoy53s什么处理器 vivoy53s处理器是骁龙多少 Macbook Pro 16寸 5500M bootcamp下安装 第三方显卡驱动(多图警告)_百... office手机版怎么改文件名 华为mate60pro+静音通话设置? 恰似寒光遇骄阳哪一章认叶绾绾 iso9000iso9001认证办理 企业ISO27001信息安全管理体系认证办理指南 实施CCRC资质认证的周期和费用? “我的反射弧特别长” | 你是情绪迟钝者吗? 难辞其咎 锐龙r7和i5哪个好? Intel酷睿i7处理器好还是AMD锐龙R5好 锐龙7和酷睿i哪个性价比高? Intel酷睿i9好还是AMD锐龙7好 志尚电风扇罩子如何打开 改装大灯交警会查吗?