Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程

这篇具有很好参考价值的文章主要介绍了Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

布局过程

Layout(布局)过程主要是确定每一个组件的布局信息(大小和位置),Flutter 的布局过程如下:

  1. 父节点向子节点传递约束(constraints)信息,限制子节点的最大和最小宽高。
  2. 子节点根据约束信息确定自己的大小(size)。
  3. 父节点根据特定布局规则(不同布局组件会有不同的布局算法)确定每一个子节点在父节点布局空间中的位置,用偏移 offset 表示。
  4. 递归整个过程,确定出每一个节点的大小和位置。

可以看到,组件的大小是由自身决定的,而组件的位置是由父组件决定的。

下面是官网的一张图,它用三句话描述了 Flutter 布局过程的精髓:

Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程

Flutter 中的布局类组件很多,根据孩子数量可以分为单子组件和多子组件,下面我们先通过分别自定义一个单子组件和多子组件来直观理解一下Flutter的布局过程,之后会介绍一下布局更新过程和 Flutter 中的 Constraints(约束)。

单子组件布局示例

我们实现一个单子组件 CustomCenter,功能基本和 Center 组件对齐,通过这个实例我们演示一下布局的主要流程。

首先,我们定义组件,为了介绍布局原理,我们不采用组合的方式来实现组件,而是直接通过定制 RenderObject 的方式来实现。因为居中组件需要包含一个子节点,所以我们直接继承 SingleChildRenderObjectWidget

class CustomCenter extends SingleChildRenderObjectWidget {
   
  const CustomCenter2({
   Key? key, required Widget child})
      : super(key: key, child: child);

  
  RenderObject createRenderObject(BuildContext context) {
   
    return RenderCustomCenter();
  }
}

接着实现 RenderCustomCenter。这里直接继承 RenderObject 会更接近底层一点,但这需要我们自己手动实现一些和布局无关的东西,比如事件分发等逻辑。为了更聚焦布局本身,我们选择继承自RenderShiftedBox,它是RenderBox的子类(RenderBox继承自RenderObject),它会帮我们实现布局之外的一些功能,这样我们只需要重写performLayout,在该函数中实现子节点居中算法即可。

class RenderCustomCenter extends RenderShiftedBox {
   
  RenderCustomCenter({
   RenderBox? child}) : super(child);

  
  void performLayout() {
   
    //1. 先对子组件进行layout,随后获取它的size
    child!.layout(
      constraints.loosen(), //将约束传递给子节点
      parentUsesSize: true, // 因为我们接下来要使用child的size,所以不能为false
    );
    //2.根据子组件的大小确定自身的大小
    size = constraints.constrain(Size(
      constraints.maxWidth == double.infinity
          ? child!.size.width
          : double.infinity,
      constraints.maxHeight == double.infinity
          ? child!.size.height
          : double.infinity,
    ));

    // 3. 根据父节点子节点的大小,算出子节点在父节点中居中之后的偏移,然后将这个偏移保存在
    // 子节点的parentData中,在后续的绘制阶段,会用到。
    BoxParentData parentData = child!.parentData as BoxParentData;
    parentData.offset = ((size - child!.size) as Offset) / 2;
  }
}

布局过程请参考注释,在此需要额外说明有3点:

  1. 在对子节点进行布局时, constraintsCustomCenter 的父组件传递给自己的约束信息,我们传递给子节点的约束信息是constraints.loosen(),下面看一下loosen的实现源码:
BoxConstraints loosen() {
   
  return BoxConstraints(
    minWidth: 0.0,
    maxWidth: maxWidth,
    minHeight: 0.0,
    maxHeight: maxHeight,
  );
}

很明显,CustomCenter 约束子节点最大宽高不超过自身的最大宽高。

  1. 子节点在父节点(CustomCenter)的约束下,确定自己的宽高;此时CustomCenter会根据子节点的宽高确定自己的宽高,上面代码的逻辑是,如果CustomCenter父节点传递给它最大宽高约束是无限大时,它的宽高会设置为它子节点的宽高。注意,如果这时将CustomCenter的宽高也设置为无限大就会有问题,因为在一个无限大的范围内自己的宽高也是无限大的话,那么实际上的宽高到底是多大,它的父节点会懵逼的!屏幕的大小是固定的,这显然不合理。如果CustomCenter父节点传递给它的最大宽高约束不是无限大,那么是可以指定自己的宽高为无限大的,因为在一个有限的空间内,子节点如果说自己无限大,那么最大也就是父节点的大小。所以,简而言之,CustomCenter 会尽可能让自己填满父元素的空间。

  2. CustomCenter 确定了自己的大小和子节点大小之后就可以确定子节点的位置了,根据居中算法,将子节点的原点坐标算出后保存在子节点的 parentData 中,在后续的绘制阶段会用到,具体怎么用,我们看一下RenderShiftedBox中默认的 paint 实现:


void paint(PaintingContext context, Offset offset) {
   
  if (child != null) {
   
    final BoxParentData childParentData = child!.parentData! as BoxParentData;
    //从child.parentData中取出子节点相对当前节点的偏移,加上当前节点在屏幕中的偏移,
    //便是子节点在屏幕中的偏移。
    context.paintChild(child!, childParentData.offset + offset);
  }
}
performLayout 流程

可以看到,布局的逻辑是在 performLayout 方法中实现的。我们梳理一下 performLayout 中具体做的事:

  1. 如果有子组件,则对子组件进行递归布局。
  2. 确定当前组件的大小(size),通常会依赖子组件的大小。
  3. 确定子组件在当前组件中的起始偏移。

在Flutter组件库中,有一些常用的单子组件比如 Align、SizedBox、DecoratedBox 等,都可以打开源码去看看其实现。

下面我们看一个多子组件的例子。

多子组件布局示例

实际开发中我们会经常用到贴边左-右布局,现在我们就来实现一个 LeftRightBox 组件来实现左-右布局,因为LeftRightBox 有两个孩子,用一个 Widget 数组来保存子组件。

首先我们定义组件,与单子组件不同的是多子组件需要继承自 MultiChildRenderObjectWidget

lass LeftRightBox extends MultiChildRenderObjectWidget {
   
  LeftRightBox({
   
    Key? key,
    required List<Widget> children,
  })  : assert(children.length == 2, "只能传两个children"),
        super(key: key, children: children);

  
  RenderObject createRenderObject(BuildContext context) {
   
    return RenderLeftRight();
  }
}

接下来需要实现 RenderLeftRight,在其 performLayout 中我们实现实现左-右布局算法:文章来源地址https://www.toymoban.com/news/detail-485222.html

class LeftRightParentData extends ContainerBoxParentData<RenderBox> {
   }

class RenderLeftRight extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, LeftRightParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, LeftRightParentData> {
   
 
  // 初始化每一个child的parentData        
  
  void setupParentData(RenderBox child) {
   
    if (child.parentData is! LeftRightParentData)
      child.parentData = LeftRightParentData();
  }

  
  void performLayout() {
   
    final BoxConstraints constraints = this.constraints;
    RenderBox leftChild = firstChild!;
    
    LeftRightParentData childParentData =
        leftChild.parentData! as LeftRightParentData;
    
    RenderBox rightChild = childParentData.nextSibling!;

    //我们限制右孩子宽度不超过总宽度一半
    rightChild.layout(
      constraints.copyWith(maxWidth: constraints.maxWidth / 2),
      parentUsesSize

到了这里,关于Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RPC核心原理(整体架构/调用过程)

    Server: Provider ,暴露服务,服务提供方 Client: Consumer ,服务消费,调用远程服务 Registry:服务注册与发现 RPC的调用过程如下: 第一步:server会将他需要暴露的服务以及他的地址信息注册到Registry这一注册中心。 第二步:client通过注册中心一只关注它所需要的服务在哪里,如果此时

    2024年02月09日
    浏览(46)
  • 弹性盒子布局 Flexbox Layout

    可以嵌套下去 1.display 属性 默认行排列 此时是 列拍 flex-direction属性——行布局 row 列布局:column flex-wrap 属性 折叠 flex-flow属性 包括 flex-direction 和flex-wrap flex-flow: row wrap 行排列 折叠 justify-content属性 元素在主轴上的对齐方式 左对齐 居中对齐 右对齐 两端对齐 拉手对齐 align

    2024年03月15日
    浏览(47)
  • Qt入门教程【Core篇】Layout布局(布局管理器、手动布局)

    😈 「CSDN主页」 :传送门 😈 「Bilibil首页」 :传送门 😈 「网易云课堂」 :传送门 😈 「CSDN学院」 :传送门 😈 「51CTO学院」 :传送门 😈 「本文的内容」 :Qt布局指南 😈 「动动你的小手」 : 点赞 👍 收藏 ⭐️ 评论 📝 界面开发首先要对整个界面进行布局,使窗体上

    2023年04月18日
    浏览(73)
  • HTML布局(HTML Layout)简介

    HTML布局(HTML Layout)简介 HTML布局(HTML Layout)是创建一个网站时需要考虑的情况,合理设计网页中内容的排列方式,让网页看起来更加合理、美观,能产生引人注目的视觉效果。页面布局一般包括: 标题:前端的一部分,用于页面顶部。header标签用于在网页中添加标题部分

    2024年02月08日
    浏览(54)
  • 【CocosCreator入门】CocosCreator组件 | Layout(布局)组件

            Cocos Creator 是一款流行的游戏开发引擎,具有丰富的组件和工具,其中的Layout组件是一种用于实现节点自适应布局的重要组件。它可以根据不同的布局方式,自动调整子节点的位置和大小,从而实现节点的自适应布局。 目录 一、组件介绍 二、组件属性 三、布局类

    2023年04月22日
    浏览(48)
  • Unity 网格布局控件-Grid Layout Group

    Unity 网格布局控件-Grid Layout Group是Unity中的UGUI控件,用于在 UI 中创建网格布局, 它的作用是:自动将子对象排列成网格,即我们可以通过该组件对子对象按行和列的形式排列,根据指定的约束条件自动调整它们的大小和位置。通常我们使用它创建具有规律排列的 UI 元素,如

    2024年02月04日
    浏览(50)
  • 【CSS 20】website layout 网站布局 页眉 导航栏 列布局 不相等栏 页脚

    website layout 网站布局 header 页眉 页眉(header)通常位于网站顶部(或顶部导航菜单的正下方) 通常包含徽标(logo)或网站名称 navbar 导航栏 导航栏包含链接列表,以帮助访问者浏览您的网站 内容 使用哪种布局通常取决于您的目标用户 最常见的布局是以下布局之一(或将它

    2024年02月13日
    浏览(83)
  • 设置layout布局嵌套路由格式正确但是看不见页面问题

    一定确保格式正确.试试重置component路径 children: [

    2024年02月13日
    浏览(55)
  • 【ArcGIS Pro二次开发】(50):布局(Layout)的基本操作

    ArcGIS Pro SDK中的布局(Layout)是用于创建和编辑打印布局的一组功能。 Layout是打印布局的容器,它可以包含多个元素,例如地图框、文本框、图例、比例尺等。 Layout中包含多种元素,比较重要的有:地图(MapFrameElement)、文本(TextElement)、图片(PictureElement)、图例(LegendElement)等。

    2024年02月16日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包