Flutter | 由Builder Widget而引发的思考

简介: 本篇主要是我实际学习中遇到的一个问题,从而引发的一些思考

概要

本篇主要是我实际学习中遇到的一个问题,从而引发的一些思考,从本篇你将学到如下:

  • Builder 神奇却又简单的背后缘由
  • BuildContext 的真实理解
  • widgetelement 的关系,及流程分析

背景

关于 Builder 这个widget,我想大家都是通过报错才发现的有这个widget的。


比如 From.of(context) ,为什么null指针(Dart新特性)了,Navigator.maybePop(context) 怎么异常了,诸如此类需要 context 传入的地方。


于是我举一个最简单的例子如下:

代码 图示


详细解释如图所示。使用Form来验证我们的输入框是否输入合格。


作为一个Flutter新手,肯定会好奇,我为啥null了呢,然后google一搜,就有人建议你使用 Builder,然后我们就会将代码改为以下方式


代码 图例


欧耶,好啦,就这么简单啊。

Builder 是什么?

官方解释:

一个无状态实用程序小部件,其[build]方法使用其[builder]回调创建小部件的子级。

源码如下

class Builder extends StatelessWidget {
  const Builder({
    Key? key,
    required this.builder,
  }) : assert(builder != null),
       super(key: key);
  @override
  Widget build(BuildContext context) => builder(context);
}

缘由


那为什么我自己的context不行呢?


让我们先去看看 Form.of 方法,当然其他of的方法也类似。

static FormState? of(BuildContext context) {
  //获取给定类型为T的最近的小部件,该类型必须是具体的[InheritedWidget]子类的类型,并向该小部件注册该构建上下文,以便在该小部件发生更改时(或引入该类型的新小部件时,或窗口小部件消失),将重新构建此构建上下文,以便它可以从该窗口小部件获取新值
  final _FormScope? scope = context.dependOnInheritedWidgetOfExactType<_FormScope>();
  return scope?._formState;
}

咳咳,简单理解 dependOnInheritedWidgetOfExactType


这个方法会根据我们传递进去的context,去从它的父级开始向上查询与当前 给定的类型匹配以及最近的这个widget,如果找到就返回,否则就抛异常。


然后 让我们将视角切换到最开始的截图,注意我圈出来的地方。



知道了缘由,甚至于你自己可以不用 Builder,自己写一个小组件也行,比如下面示例。


示例代码 动画

思考

但是到这里就结束了吗?如果对于本篇而言,的确是。但对我自己而言,却带来了更多疑问:

  • context 到底是干什么的?
  • build(context) 方法中的 BuildContext 是哪里来的?

Widget和Element的关系

我们常听说 Flutter有三棵树,也就是 Widget , Element ,RenderObject ,我们主要关心前两者。


Widget 树,顾名思义,就是我们常用的组件,其仅仅相当于我们对 UI 元素的一个 配置。


Element,是Widget 实际对应的对象。why?没懂,没错,其实我也没明白😂

我们通过源码分析一下,Flutter的源码相比Android原生,是非常简单好理解。


我们以常使用的 StatelessWidget 为例来看看。

StatelessWidget

abstract class StatelessWidget extends Widget {
  ...
  @override
  StatelessElement createElement() => StatelessElement(this);
  @protected
  Widget build(BuildContext context);
}

我们常见的 build(context) 方法,是其定义的一个抽象方法;

其实现了 Widget 的抽象方法 createElement(),并传入了我们当前实例对象,所以继续往下看。


StatelessElement

class StatelessElement extends ComponentElement {
  /// Creates an element that uses the given widget as its configuration.
  StatelessElement(StatelessWidget widget) : super(widget);
  @override
  StatelessWidget get widget => super.widget as StatelessWidget;
  //划重点
  @override
  Widget build() => widget.build(this);
}

我们主要看 build() 方法,其调用的我们 StatexxWidget-build 方法,其实现widget的构建,并传入了一个this,也就是一个StatelessElement,但是我们最终拿到的都是 BuildContext啊?这是为什么呢,我们继续去看 ComponentElement。

Widget build(BuildContext context)

ComponentElement

//用于构建窗口小部件
abstract class ComponentElement extends Element 

Element

  abstract class Element extends DiagnosticableTree implements BuildContext 

原来Element实现了 BuildContext 接口,所以在StatelessElement-build()中,可以直接传递element进去。


我们看一下官方对 Element 的解释:



简而言之,就是,Element 代表了 Widget 在树中实际位置的实例对象,为什么这么说呢?


因为Widget实际上就是Element的配置数据,Widget 树也就是一个配置树,而真正的 UI 渲染树是由Element构成;不过,由于Element是通过Widget生成的,所以它们之间有对应关系;相应的,一个Widget对象可以对应多个Element对象。这也很好理解,根据同一份配置(Widget),可以创建多个实例(Element)。


BuildContext



我们可以理解为 BuildContext 对象实际就是 Widget对应的 Element对象.所以我们可以通过 context 在StatelessWidget 和 StatefulWidget 的build方法来间接的访问element对象(通过各种xx.of),而我们开发中 widget的组合使用,比如各种Widget的搭配,由它们形成了我们的配置树,而这个widget最终会一一对应一个 element,从而形成了一个Emelent树。


但为什么build方法里,不直接定义成Element对象,却要定义为BuildContext?


官方的解释如下:


BuildContext 用于阻止对 Element 的直接操作。


显然这个解释并不是怎么好理解,在反复思考及挠头后,我个人的理解如下:


在软件开发中,任意复杂耦合的两个事物之间都可以通过一个第三者进行解耦,而BuildContext就是如此。

因为我们的 Element 承担了widget 实际的对应对象,相应的其有很多初始化及其他方法是不便于我们开发者直接调用的,如果将其直接暴露出去,相应的复杂度会大大提示,所以它通过 BuildContext这个接口,并定义了相应的一些操作 Element 的方法,虽然一定程度上来说,我们依然能间接操作 element,但是通过这种第三者的方式,很好的屏蔽了一些特性,对于我们开发者而言,只需关注widget即可,对于element相关的操作,可以通过相应Widget的xx.of() 方法,极大程度上让我们开发者可以更专注的应该widget层的开发,而无需关注其他方面。

总结

1.context?

context是什么?

我们常用的 widget 只是一个配置信息,实际每一个 widget 都对应了一个 Element,由于 Element 实现了 Buildcontext ,所以在 StatexxxWidget-build(context) 方法里,通过 context ,我们可以间接的操作 Emelent 去进行一些操作,比如 xx.of(context) 内部正是调用了 dependOnInheritedWidgetOfExactType (即从Element父级开始寻找匹配的widget),所以我们可以认为:


context实际就是我们widget在Element树中对应的实际位置。

2.build(context)?

build(context) 方法中的 BuildContext 是哪里来的?


这个问题实际上就是对源码做了一个简单概括:


  • 我们常用的 StatelessWidget 或者 StatefulWidget,其内部 build() 或者后者 State-build() 方法,都是返回一个 Widget 对象;
  • 而上述的两个组件都继承自 Widget ,有一个 createElement() 的抽象方法,此方法默认是返回了一个 StatexxxElement(this),其 this 代表当前 widget 实例;
  • 而 StatelessElement 继承自 ComponentElement ,其 build() 默认是调用了我们 Widget 中的 build(context) 方法从而实现了初始化;
  • 由于 ComponentElement 继承自 Element ,Element 实现了 BuildContext 接口,所以我们可以在 Widget 的 build(context) 方法中拿到 BuildContext;
目录
相关文章
|
15天前
深入理解Flutter鸿蒙next版本 中的Widget继承:使用extends获取数据与父类约束
本文详细介绍了Flutter中如何通过继承其他Widget来创建自定义组件。首先解释了Widget继承的基本概念,包括StatelessWidget和StatefulWidget的区别。接着通过具体示例展示了如何继承StatelessWidget和StatefulWidget,并在子类中访问父类的build方法和状态。最后,结合多个自定义Widget展示了如何在实际应用中灵活使用继承和组合来构建复杂的UI。
66 8
|
13天前
|
容器
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
在 Flutter 中,状态管理至关重要。本文详细介绍了如何使用 InheritedWidget 实现跨 Widget 的状态传递。InheritedWidget 允许数据在 Widget 树中向下传递,适用于多层嵌套的场景。通过一个简单的计数器示例,展示了如何创建和使用 InheritedWidget,包括其基础概念、工作原理及代码实现。虽然 InheritedWidget 较底层,但它是许多高级状态管理解决方案的基础。
88 2
|
1月前
|
容器
flutter:第一个flutter&Widget的使用 (二)
本文介绍了Flutter框架下的基本组件及其用法,包括简单的 Stateless Widget 如文本和按钮,以及更复杂的 StatefulWidget 示例。详细解释了如何使用 `context` 获取祖先小部件的信息,并展示了 `MaterialApp` 的属性及用途。此外,还探讨了 `StatefulWidget` 与 `StatelessWidget` 的区别,以及 `AppBar` 的常见属性配置方法。适合Flutter初学者参考学习。
|
13天前
|
Dart JavaScript 前端开发
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
Flutter 是 Google 开发的开源 UI 框架,用于快速构建高性能的移动、Web 和桌面应用。Flutter 通过 Widget 构建 UI,每个 UI 元素都是 Widget,包括文本、按钮、图片等。Widget 不仅描述外观,还描述行为,是不可变的。常见的 Widget 包括结构型(Container、Column、Row)、呈现型(Text、Image)、交互型(ElevatedButton)和状态管理型(StatefulWidget)。Flutter 与鸿蒙 Next 在组件化架构、开发语言、布局系统、性能和跨平台支持方面各有优势
64 0
|
4月前
Flutter-底部弹出框(Widget层级)
文章描述了如何在Flutter中使用DraggableScrollableSheet创建一个底部弹出框,同时保持其可手势滑动关闭。作者遇到问题并提出对原控件进行扩展,以支持头部和列表布局的滑动关闭功能。
180 0
|
5月前
Flutter StreamBuilder 实现局部刷新 Widget
Flutter StreamBuilder 实现局部刷新 Widget
43 0
|
6月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
6月前
|
开发框架 前端开发 搜索推荐
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程
【4月更文挑战第30天】探索Flutter的自定义Widget与渲染流程。自定义Widget是实现复杂UI设计的关键,优点在于个性化设计、功能扩展和代码复用,但也面临性能优化和复杂性管理的挑战。创建步骤包括设计结构、定义Widget类、实现构建逻辑和处理交互。Flutter渲染流程涉及渲染对象树、布局、绘制和合成阶段。实践案例展示如何创建带渐变背景和阴影的自定义按钮。了解这些知识能提升应用体验并应对开发挑战。查阅官方文档以深入学习。
76 0
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程
|
6月前
|
JavaScript 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
【4月更文挑战第30天】本文探讨了Flutter的Widget和状态管理。Widget是Flutter构建UI的基础,分为有状态和无状态两种。状态管理确保UI随应用状态变化更新,影响应用性能和可维护性。文章介绍了`setState`、`Provider`、`Riverpod`、`Bloc`和`Redux`等状态管理方法,并通过计数器应用展示了其实现。选择合适的状态管理策略对高效开发至关重要。
85 0
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
|
6月前
|
编解码 算法 开发者
Flutter的布局系统:深入探索布局Widget与布局原则
【4月更文挑战第26天】Flutter布局系统详解,涵盖布局Widget(Row/Column、Stack、GridView/ListView、CustomSingleChildLayout)和布局原则(弹性布局、约束优先、流式布局、简洁明了)。文章旨在帮助开发者理解并运用Flutter的布局系统,创建适应性强、用户体验佳的界面。通过选择合适的布局Widget和遵循原则,可实现复杂且高效的UI设计。