Flutter Widget源码解析及实战(一)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Flutter Widget源码解析及实战(一)

Widget


在flutter中所有页面展示出来的元素都是由一个个的widget组成,与原生android开发不同的地方在于flutter中widget不仅仅表示UI元素,他也可以是一个完全和UI无关如GestureDetector,GestureDetector继承自StatelessWidget。


Widget的功能类似于原生android开发中的style文件,用来描述UI的样式,最终真正绘制在屏幕上的是Element。

@immutableabstract class Widget extends DiagnosticableTree { // DiagnosticableTree 诊断树主要作用是提供调试信息    const Widget({ this.key }); // key 
  // 最终整个绘制流程在Element中进行  @protected  Element createElement();
  @override  String toStringShort() {    return key == null ? '$runtimeType' : '$runtimeType-$key';  }
  // 提供一些调试用的信息  @override  void debugFillProperties(DiagnosticPropertiesBuilder properties) {    super.debugFillProperties(properties);    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;  }
  // 这里通过判断key和runtimeType是否一致来判断是否可以更新UI  static bool canUpdate(Widget oldWidget, Widget newWidget) {    return oldWidget.runtimeType == newWidget.runtimeType        && oldWidget.key == newWidget.key;  }}


StatelessWidget


无状态的widget一般用于一些静态UI的绘制(例如:Text)或者提供与UI无关的功能(例如:GestureDetector用来管理手势相关的功能),源码如下:


abstract class StatelessWidget extends Widget {    const StatelessWidget({ Key key }) : super(key: key);    @override  StatelessElement createElement() => StatelessElement(this);    @protected  Widget build(BuildContext context);  }


StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget,具体如下:


class TestLess extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Container(      child: Text("test"),    );  }}

StatefulWidget


可变状态的小部件

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
  @override  StatefulElement createElement() => StatefulElement(this);
  @protected  State createState();}


与StatelessWidget不同的是StatefulWidget类中添加了一个新的接口createState(),一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态。下面是StatefulWidget的最佳实践:


  • 尽量将需要该表状态的widget防止在子节点,这样在改变整个渲染树的时候就只需要更新一个widget即可,如果将其防止在父节点那么将会导致当前节点的整个子节点的widget都会被重新渲染。
  • 尽量减少build方法中返回的widget的嵌套层级,理想情况下一个StatefulWidget仅仅只包含一个类型为RenderObjectWidget的子widget。例如:RichText,但显然这是不切实际的,但一个小部件越是接近这个理想,效率越高。
  • 如果子树没有更改,请缓存表示该子树的窗口小部件,并在每次使用时重新使用它。对于要重新使用的窗口小部件,要比创建新的(但配置相同的)窗口小部件更有效。将有状态部分分解为带有子参数的小部件是执行此操作的常用方法。
  • 尽可能使用`const`小部件。(这相当于缓存窗口小部件并重新使用它。)
  • 避免更改任何创建的子树的深度或更改子树中任何窗口小部件的类型。例如,不是返回包含在[IgnorePointer]中的子项或子项,而是始终将子窗口小部件包装在[IgnorePointer]中并控制[IgnorePointer.ignoring]属性。这是因为更改子树的深度需要重建,布局和绘制整个子树,而只更改属性将需要对渲染树进行尽可能少的更改(例如,在[IgnorePointer]的情况下,没有布局)或重绘是必要的)。
  • 如果由于某种原因必须更改深度,请考虑将子树的公共部分包装在具有[GlobalKey]的小部件中,该[GlobalKey]在有状态小部件的生命周期内保持一致。(如果没有其他小部件可以方便地分配密钥,[KeyedSubtree]小部件可能对此有用。)


下面是一个名为`YellowBird`的有状态小部件子类的框架。在这个例子中[State]没有实际状态。State通常表示为私人成员字段。此外,通常小部件有更多的构造函数参数,每个参数都应该为`final`类型。

class YellowBird extends StatefulWidget {   const YellowBird({ Key key }) : super(key: key);
   @override   _YellowBirdState createState() => _YellowBirdState(); }
 class _YellowBirdState extends State<YellowBird> {   @override   Widget build(BuildContext context) {     return Container(color: const Color(0xFFFFE306));   } }


下面的例子显示了更通用的小部件`Bird`,它可以被赋予一种颜色和一个子widget,并且它有一些内部状态,可以调用一个方法来改变它。

class Bird extends StatefulWidget {   const Bird({     Key key,     this.color = const Color(0xFFFFE306),     this.child,   }) : super(key: key);
   final Color color;   final Widget child;
   _BirdState createState() => _BirdState(); }
 class _BirdState extends State<Bird> {   double _size = 1.0;
   void grow() {     setState(() { _size += 0.1; });   }
   @override   Widget build(BuildContext context) {     return Container(       color: widget.color,       transform: Matrix4.diagonal3Values(_size, _size, 1.0),       child: widget.child,     );   } }

按照惯例,窗口小部件构造函数仅使用命名参数。可以使用[@required]将命名参数标记为必需。按照惯例,第一个参数是[key],最后一个参数是`child`,`children`。


StatefulWidget生命周期


image.png

State中有两个常用属性


  • widget  :表示与State实例相关联的widget实例
  • BuildContext:构建widget的上下文
  • initState:当Widget第一次插入到Widget树时会被调用,对于每一个State对象。framework将在创建的每个[State]对象调用此方法一次。重写此方法以执行初始化,该初始化取决于此对象插入树中的位置(即[context])或用于配置此对象的窗口小部件(即[widget])。如果[State]的[build]方法依赖于一个本身可以改变状态的对象,例如[ChangeNotifier]或[Stream],或者一个可以订阅接收通知的其他对象,那么一定要订阅并在[initState],[didUpdateWidget]和[dispose]中取消订阅:您不能使用此方法中的[BuildContext.inheritFromWidgetOfExactType]。但是,[didChangeDependencies]将在此方法之后立即调用,可以在那里使用[BuildContext.inheritFromWidgetOfExactType]。
  • didChangeDependencies:当State对象的依赖发生变化时会被调用,如果父Widget重建并请求树中的此位置更新以显示具有相同[runtimeType]和[Widget.key]的新Widget,则框架将更新此[State]对象的[widget]属性以引用新Widget然后使用上一个Widget作为参数调用此方法。覆写此方法可以在[widget]更改时进行响应(例如,开始隐式动画)。在调用[didUpdateWidget]之后,框架总是调用[build],这意味着对[didUpdateWidget]中的[setState]的任何调用都是多余的。
  • build:它主要是用于构建Widget子树
  • reassemble:此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用。
  • didUpdateWidget:在widget重新构建时,framework会调用canUpdate来检测Widget树中同一位置的新旧节点,然后决定是否需要更新。
  • deactivate:当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
  • dispose:当State对象从树中被永久移除时调用;通常在此回调中释放资源。
相关文章
|
5天前
|
Dart Android开发
鸿蒙Flutter实战:05-使用第三方插件
在鸿蒙Flutter开发中,使用原生功能需借助插件。可自编原生ArkTS代码或采用第三方插件。自编代码通过PlatformView或MethodChannel实现;第三方插件需确保适配鸿蒙,否则须配置替代插件或自行开发。
26 1
鸿蒙Flutter实战:05-使用第三方插件
|
5天前
|
Java 开发工具
鸿蒙Flutter实战:02-Windows环境搭建踩坑指南
本指南介绍如何搭建鸿蒙Flutter开发环境,包括下载Flutter SDK、配置环境变量(如FLUTTER_STORAGE_BASE_URL、PUB_HOSTED_URL、DEVECO_SDK_HOME等)和检查工具版本。还提到避免项目路径过深、与SDK同盘存放等注意事项,以及解决VsCode无法识别设备的方法。
20 0
|
5天前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
18 3
|
5天前
|
Web App开发 JavaScript 前端开发
鸿蒙Flutter实战:04-如何使用DevTools调试Webview
本文介绍如何在鸿蒙 Flutter 开发中调试 Webview,包括配置允许调试、找到 devtools 端口、开启端口转发、在 Chrome 中调试 Webview等。
12 0
鸿蒙Flutter实战:04-如何使用DevTools调试Webview
|
5天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
15 0
|
5天前
|
分布式计算 Java 应用服务中间件
NettyIO框架的深度技术解析与实战
【10月更文挑战第13天】Netty是一个异步事件驱动的网络应用程序框架,由JBOSS提供,现已成为Github上的独立项目。
16 0
|
5天前
|
缓存 Dart IDE
鸿蒙Flutter实战:10-常见问题集合
本文介绍了鸿蒙 Flutter 开发的学习路径,包括掌握 Flutter 和鸿蒙基础知识,解决 MatePad 适配、模拟器异常、debug 版本错误等问题,并提供了更换 App 图标和名称的方法及环境变量配置指导。
24 0
|
5天前
|
Dart IDE 开发工具
鸿蒙Flutter实战:09-现有Flutter项目支持鸿蒙
本文介绍了将现有Flutter项目适配鸿蒙系统的步骤。首先,根据[鸿蒙Flutter适配指南]搭建开发环境,并使用fvm管理多版本SDK。项目采用模块化设计,包括apps、common、components、modules和plugins等目录,分别对应不同功能模块。重点在于在apps目录下创建新的鸿蒙项目,逐步添加依赖并解决版本兼容性问题。最后,通过配置pubspec.yaml文件和特定插件的鸿蒙化适配,完成项目的编译与运行测试。
18 0
|
5天前
|
IDE 调度 开发工具
鸿蒙Flutter实战:08-如何调试代码
本文介绍了鸿蒙Flutter项目的开发环境搭建、配置、日志查看及调试方法。首先按照指南搭建开发环境,安装IDE插件;接着配置vscode的launch.json文件;通过IDE调试控制台或命令行查看日志;提供两种调试Flutter的方式,包括IDE直接运行和使用DevEco;最后介绍ArkTs和Webview的调试方法。
14 0
|
5天前
|
编解码 Dart API
鸿蒙Flutter实战:06-使用ArkTs开发Flutter鸿蒙插件
本文介绍了如何开发一个 Flutter 鸿蒙插件,实现 Flutter 与鸿蒙的混合开发及双端消息通信。通过定义 `MethodChannel` 实现 Flutter 侧的 token 存取方法,并在鸿蒙侧编写 `EntryAbility` 和 `ForestPlugin`,使用鸿蒙的首选项 API 完成数据的读写操作。文章还提供了注意事项和参考资料,帮助开发者更好地理解和实现这一过程。
23 0

推荐镜像

更多