文章图片
然而,你有没有想过幕后发生了什么?如何做小部件形成各种形状,文字和图片,我们在屏幕上看到了什么?如何理解Flutter中的RenderObject?
这些问题至关重要,它们的答案将大大帮助你成为更好的 Flutter 开发人员。正确理解 Flutter 小部件转换为我们看到并与之交互的 UI 屏幕的步骤将进一步帮助我们正确使用可用资源来满足特殊需求,例如创建自定义布局、超级自定义绘画等。
本文旨在带你逐步了解 Flutter 表面(小部件)下的奥秘。
Flutter 中的渲染是如何工作的?Flutter如何使用RenderObject?在我们开始介绍 RenderObjects 及其用途、功能和重要性之前,让我们快速了解一下 Flutter 中的渲染是如何发生的。
Flutter 使用在传递给小部件的字段或参数中保存配置信息的小部件。这里的小部件充当各种“容器”:它保存这些配置参数但不使用它们。小部件实例化并膨胀为一个元素。
这个元素被插入到元素树中并代表小部件,元素树中的每个元素都有一个附加的RenderObject。这些 RenderObjects 负责控制这些配置参数,如尺寸、布局和小部件在屏幕上的绘制,形成我们看到的 UI。
文章图片
了解 RenderObjects 肯定会帮助你构建高质量的移动应用程序。那么这些RenderObject到底是什么呢?
什么是RenderObjects?如何理解Flutter中的RenderObject?RenderObjects 是那些负责控制大小、布局和逻辑的特定“对象”,用于将小部件绘制到屏幕并形成应用程序的 UI。你可以说实际渲染发生在RenderObjects 中。
但它们很少使用,因为在十分之九的情况下,开发人员不需要使用它们。小部件足以满足大多数开发人员的需求。
但是,在某些特定情况下,超级复杂的设计需要精确实现。使用小部件或通过使用更多香料从头开始构建特定小部件或使其更易于使用可能不是完全可能的。在这样的情况下,RenderObjects 将是正确的使用工具。
了解运行中的 RenderObjects:作为案例研究的不透明度小部件让我们看一下Opacity 小部件,以更好地了解从小部件到元素再到 RenderObject 的链接。不透明度小部件调整其子项的透明度。
关于小部件将扩展的 RenderObject 类型需要注意的一些关键事项:
- 如果一个小部件的子部件个数为零,则它扩展LeafRenderObjectWidget
- 如果它有一个孩子,它扩展SingleChildRenderObjectWidget
- 如果它有两个或更多的孩子,它扩展MultiChildRenderObjectWidget
反过来,SingleChildRenderObjectWidget 扩展了 RenderObjectWidget。最后,RenderObjectWidget 扩展了 Widget 类。
//Opacity extends SingleChildRenderObjectWidget
class Opacity extends SingleChildRenderObjectWidget {// SingleChildRenderObjectWidget extends RenderObjectWidget
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {// RenderObjectWidget extends Widget
abstract class RenderObjectWidget extends Widget {
那么,我们为什么要看谁扩展了什么?SingleChildRenderObjectWidget 类有一个方法负责创建元素。回想一下,特定小部件的元素是它的实例,并指向它在元素树上的位置。它附加到小部件。该元素是 SingleChildRenderObjectElement 并且是树上的 Opacity 小部件的实例。
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
const SingleChildRenderObjectWidget({Key? key, this.child}) : super(key: key);
final Widget? child;
@override
SingleChildRenderObjectElement createElement() =>
SingleChildRenderObjectElement(this);
}
回到 Opacity 小部件,它公开了为这个特定小部件创建和更新 RenderObject 的两个基本方法。
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
@override
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
renderObject
..opacity = opacity
..alwaysIncludeSemantics = alwaysIncludeSemantics;
}
该
createRenderObject
方法返回RenderOpacity
类。的RenderOpacity
类需要在配置参数,这是不透明度在0.0和1.0之间的范围内。RenderOpacity 扩展了RenderProxyBox 类,它提供了对子部件执行不同操作的方法——其中最重要的是
paint()
方法。@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
if (_alpha == 0) {
layer = null;
return;
}
if (_alpha == 255) {
layer = null;
context.paintChild(child!, offset);
return;
}
assert(needsCompositing);
layer = context.pushOpacity(offset, _alpha, super.paint, oldLayer: layer as OpacityLayer?);
}
}
如何理解Flutter中的RenderObject?Paint 方法执行必要的检查和断言,然后使用
context.pushOpacity
. 这就是主要操作发生的地方,所以即使我们有 Opacity 小部件及其相应的元素,绘制发生在 RenderObjects 中。它们在 Flutter 表面之下发生的事情的过程中是非常重要的。现在我们已经了解了 RenderObjects,让我们看看如何使用自定义 RenderObjects 创建小部件以满足我们的需求。
如何创建自己的RenderObjectFlutter如何使用RenderObject?本节将介绍创建自定义小部件的分步过程 - 我们将创建一个 Gap 小部件 - 及其 RenderObject,它将负责在屏幕上绘制布局。
间隙小部件是在树中的小部件之间创建空间或间隙的小部件。与SizedBox 类不同,Gap 不需要连续设置大小,而是推断它应该是什么大小。它通过检查其父级的布局,然后根据布局创建间隙来做到这一点。
Gap 小部件只接受一个属性,
mainAxisExtent
即我们需要的小部件之间的空间量。Flutter RenderObject用法示例:我们需要做的第一件事是创建 RenderObject,它将执行实际的布局
_RenderGap
。它 extends RenderBox
,它扩展了 RenderObject。(另一种是RenderSliver
, 当我们需要可滚动的内容时使用。)abstract class RenderBox extends RenderObject {
所述
_RenderGap
接受传递的值,并将其设置到mainAxisExtent
参数;
它还调用该markNeedsLayout()
方法,该方法告诉 Flutter 特定值已更改,Flutter 需要再次运行performLayout()
方法。class _RenderGap extends RenderBox {
_RenderGap({
double? mainAxisExtent,
}) : _mainAxisExtent = mainAxisExtent!;
double get mainAxisExtent => _mainAxisExtent;
double _mainAxisExtent;
set mainAxisExtent(double value) {
if (_mainAxisExtent != value) {
_mainAxisExtent = value;
markNeedsLayout();
}
}
@override
void performLayout() {
final AbstractNode flex = parent!;
if (flex is RenderFlex) {
if (flex.direction == Axis.horizontal) {
size = constraints.constrain(Size(mainAxisExtent, 0));
} else {
size = constraints.constrain(Size(0, mainAxisExtent));
}
} else {
throw FlutterError(
'Gap widget is not inside a Flex Parent',
);
}
}
}
该
performLayout
方法做了两个关键的事情:- 检查父级的布局方向
- 根据这些结果,它通过调用垂直或水平方向的约束来设置 Gap 小部件的大小
class Gap extends LeafRenderObjectWidget {
const Gap(
this.mainAxisExtent, {
Key? key,
}) : assert(mainAxisExtent >= 0 &
&
mainAxisExtent <
double.infinity),
super(key: key);
final double mainAxisExtent;
}
回想一下我们之前提到的基于子组件的数量应该扩展哪些小部件;由于 Gap 小部件不接受任何孩子,它扩展了 LeafRenderObjectWidget,接受并对其执行两项检查:
mainAxisExtent
value
- 检查它是否大于零——我们不希望应用程序中有负间距,而这个检查消除了这种可能性。如果我们的值小于零,Flutter 会抛出异常
- 检查值是否小于- 我们不想要一个永远持续的 Gap 空间
double.infinity
_RenderGap
对我们而言):- 该
createRenderObject
方法返回 RenderObject,即_RenderGap
,并传递mainAxisExtent
我们想要的值 - 该
updateRenderObject
方法接收_RenderGap
并更新mainAxisExtent
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderGap(mainAxisExtent: mainAxisExtent);
}
@override
void updateRenderObject(BuildContext context, _RenderGap renderObject) {
renderObject.mainAxisExtent = mainAxisExtent;
}
我们已经成功设置了 Gap 小部件!现在,让我们构建一个简单的 UI 来在实践中展示它。
在实践中使用我们的 Gap 小部件Gap 小部件使用我们指定的尺寸为我们的 UI 添加间距 例如,如果我们当前位于 Column 小部件中,则 Gap 小部件会推断其父级(列小部件)具有垂直方向,因此,在绘制期间,它会进行布局本身在垂直方向,这意味着它创造了一个垂直的空间。如果父小部件具有水平方向,则它在水平方向上布局。
Flutter如何使用RenderObject?让我们构建一个简单的屏幕来显示它的运行情况。我们的屏幕将同时具有 Row 和 Column 小部件,并且每个小部件中都有一个 Gap 小部件,因此我们可以看到它如何响应垂直和水平布局。
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
/// Creates a [
HomePage].
const HomePage({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <
Widget>[
const Text('This is testing the Gap widget'),
const Gap(30),
const Text(
'Notice the gap between me and the text above me, its vertical'),
const Gap(30),
const Text('Now lets look at it working horizontally'),
const Gap(16),
Row(
children: const [
Text('First Text inside the Row'),
Gap(16),
Text(
'Second Text inside Row',
maxLines: 3,
),
],
),
],
),
),
),
);
}
}
我们传入我们想要的间距值,而不指定它是水平空间还是垂直空间;间隙小部件应检查父小部件的方向,并相应地将间隙空间呈现为水平或垂直。
保存并运行你的应用程序。你应该会看到差距及其对应用程序布局的影响。
文章图片
如果你不想自己编写,你还可以下载将提供 Gap 小部件的软件包。但是,从头开始构建一个结构可以为你提供更好的结构灵活性,因为你可以对其进行调整以适应你的需求。它还可以帮助你更好地了解整个过程以及它是如何组合在一起形成小部件的。
结论呼,我们做到了!我们成功地创建了我们的 RenderObject 并用它来构建一个小部件来满足我们的需求(好吧,它让生活更轻松——你会同意)。如何理解Flutter中的RenderObject?希望你已经成功了解了 Flutter 中的 RenderObject,它的用途是什么,以及它们如何帮助构建小部件,为我们提供应用程序所需的特殊功能。
【如何理解Flutter中的RenderObject (详细解析)】最重要的是,你已经了解了 Flutter 应用程序表面之下发生的事情,在小部件的世界中。本文为你提供了成为世界级开发人员所需的另一种工具。使用它,你会看到效果。祝你有美好的一天。
推荐阅读
- 如何在Flutter中创建图像轮播(分步指南)
- Dart和Flutter数据结构用法示例综合指南
- 如何在Next.js中构建站点地图生成器()
- 端口·木马·安全·扫描应用知识
- MPLS技术基本原理
- MPLS进展简史
- MPLS技术及规范化进展
- Debug命令大全
- 服务器角色换位“乾坤大挪移心经”