Flutter 必须知道的布局规则


很多人说布局不熟练,我建议是先去了解布局规则,我之前的文章有讲过。

今天看到一文写的很好,我做了翻译,时间关系没有润色太多,然后我也准备了视频做了解说,希望能帮到你。

原文 https://rohanjariwala03.medium.com/flutter-the-advanced-layout-rule-even-beginners-must-know-87eebb54cef6

前言

当学习 Flutter 的人问你为什么某个 widget 有宽度: 100 不是 100 像素宽,默认的答案是告诉他们把这个 widget 放在一个 center ,对不对?

别这样。

如果你这样做了,他们会一次又一次地问你为什么有些 FittedBox 不能工作,为什么那个 Column 会溢出,或者 IntrinsicWidth 应该做什么。

相反,首先告诉他们 Flutter 的布局与 HTML 的布局非常不同(这可能是他们来自哪里) ,然后让他们记住以下规则:

约束向下传递,尺寸向上传递,位置由父组件决定。

如果不知道这个规则,就不能真正理解 Flutter 布局,所以我相信每个人都应该尽早学习它。

细节:

  • widget 从其父部件获得自己的约束。“约束”只是一组 4 个双精度: 最小和最大宽度,以及最小和最大高度。
  • 然后, widget 遍历它自己的子列表。 widget 一个接一个地告诉它的子元素它们的约束条件是什么(每个子元素的约束条件可能不同) ,然后询问每个子元素它希望是什么大小。
  • 然后, widget 将其子元素(水平放在 x 轴上,垂直放在 y 轴上)一个接一个地放置。
  • 最后, widget 告诉它的父部件它自己的大小(当然是在原始约束内)。

例如,如果一个 widget 像一个带有一些填充的列,并且想要布局它的两个子元素:


widget - 嘿, parent ,我的约束条件是什么?

父级 - 像素宽度必须在 90 到 300 之间,身高必须在 30 到 85 之间。

Widget ー嗯,因为我想要有 5 个像素的填充,那么我的 child 最多可以有 290 个像素的宽度和 75 个像素的高度。

widget - 嘿,第一个 child ,你必须从 0 到 290 像素宽,0 到 75 高。

第一个 child - 好的,那么我希望是 290 像素宽,20 像素高。

Widget ー 嗯,因为我想把我的第二个 child 放在第一个 child 的下面,所以我的第二个 child 只有 55 像素的高度。

widget - 嘿,第二个 child ,你必须从 0 到 290 宽,0 到 55 高。

第二个 child - 好的,我希望是 140 像素宽,30 像素高。

widget ー 很好。我把第一个 child 放在 x: 5 和 y: 5 的位置,第二个 child 放在 x: 80 和 y: 25 的位置。

widget - 嘿, parent ,我已经决定我的尺寸是 300 像素宽,60 像素高。

Limitations 限制

由于上面描述的布局规则,Flutter 的布局引擎有一些重要的局限性:

  • widget 只能在其父节点给它的约束范围内决定自己的大小。这意味着一个 widget 通常不能有它想要的任何大小。
  • 一个 widget 不能知道也不能决定它自己在屏幕上的位置,因为决定 widget 位置的是 widget 的父部件。
  • 由于父节点的大小和位置依次取决于它自己的父节点,因此如果不考虑整个树,就不可能精确地定义任何 widget 的大小和位置。

代码

https://github.com/ducafecat/flutter_layout_article

例子 1


Container(color: Colors.red)

屏幕是 Container 的父级。它强制红色 container 与屏幕大小完全相同。

所以 container 填满了屏幕,变成了红色。

例子 2


Container(width: 100, height: 100, color: Colors.red)

红色的 container 想要 100 × 100,但是它不能,因为屏幕强迫它和屏幕尺寸完全一样。

所以 container 填满了屏幕。

例子 3


Center(   child: Container(width: 100, height: 100, color: Colors.red))

屏幕强制 center 与屏幕大小完全相同,因此 center 填充了整个屏幕。

center 告诉 container 它可以是它想要的任何大小,但是不能大于屏幕。现在的 container 确实可以达到 100 × 100。

例子 4


Align(   alignment: Alignment.bottomRight,   child: Container(width: 100, height: 100, color: Colors.red),)

这与前面的示例不同,因为它使用 Align 而不是 Center。

Align 还告诉 Container 它可以是它想要的任何大小,但是如果有空空间,它将不会居中 Container,而是将其对齐到可用空间的右下角。

例子 5


Center(   child: Container(      color: Colors.red,      width: double.infinity,      height: double.infinity,   ))

屏幕强制 center 与屏幕大小完全相同,因此 center 填充了整个屏幕。

center 告诉 container 它可以是它想要的任何大小,但是不能大于屏幕。 container 希望是无限大小,但由于它不能大于屏幕,它只会填满屏幕。

例子 6


Center(child: Container(color: Colors.red))

屏幕强制 center 与屏幕大小完全相同,因此 center 填充了整个屏幕。

center 告诉 container 它是任何大小,它想要的,但不超过屏幕。因为 Container 没有子元素,也没有固定的大小,所以它决定要尽可能地大,因此它适合整个屏幕。

但是为什么 container 决定这样做呢?仅仅是因为这是那些创建 Container widget 的人的设计决策。它可以以不同的方式创建,实际上您必须阅读 Container 的文档,以了解它将根据具体情况做什么。

例子 7


Center(   child: Container(      color: Colors.red,      child: Container(color: Colors.green, width: 30, height: 30),   ))

屏幕强制 center 与屏幕大小完全相同,因此 center 填充了整个屏幕。

center 告诉红色 container 它可以是任意大小,但不能大于屏幕。由于红色 container 没有大小,但是有一个子 container ,所以它决定希望其子 container 的大小相同。

红色 container 告诉它的子 container 可以是任意大小,但不能大于屏幕。

child 刚好是一个绿色的 container ,要 30 × 30。如上所述,红色的 container 将自己大小的子大小,所以它也将是 30 × 30。不可见红色,因为绿色 container 将占据所有红色 container 。

例子 8


Center(   child: Container(     color: Colors.red,     padding: const EdgeInsets.all(20.0),     child: Container(color: Colors.green, width: 30, height: 30),   ))

红色 container 将根据其子 container 大小调整自身大小,但是它会考虑自己的填充。因此,它将是 70 × 70(= 30 × 30 加上 20 像素的填充所有方面)。由于填充,红色将可见,绿色 container 的大小将与前面的示例相同。

例子 9


ConstrainedBox(   constraints: BoxConstraints(      minWidth: 70,      minHeight: 70,      maxWidth: 150,      maxHeight: 150,   ),   child: Container(color: Colors.red, width: 10, height: 10),)

您可能会猜测 container 必须在 70 到 150 像素之间,但是您可能错了。ConstrainedBox 只会施加比它从其父类接收到的约束更多的约束。

在这里,屏幕强制 ConstrainedBox 与屏幕大小完全相同,因此它将告诉其子 container 也假定屏幕大小,从而忽略其约束参数。

例子 10


Center(   child: ConstrainedBox(      constraints: BoxConstraints(         minWidth: 70,         minHeight: 70,         maxWidth: 150,         maxHeight: 150,      ),      child: Container(color: Colors.red, width: 10, height: 10),   ))

现在,Center 将允许 ConstrainedBox 的大小不超过屏幕的大小。ConstrainedBox 将把其约束参数中的附加约束强加给它的子约束。

所以 container 必须在 70 到 150 像素之间。它希望有 10 个像素,所以它最终将有 70(最低)。

例子 11


Center( center ( child: ConstrainedBox( Child: ConstrainedBox ( constraints: BoxConstraints( 限制条件: minWidth: 70,最小宽度: 70, minHeight: 70, 身高: 70, maxWidth: 150,最大宽度: 150, maxHeight: 150, 身高: 150, ), child: Container(color: Colors.red, width: 1000, height: 1000), child: Container (颜色: Colors.red,宽度: 1000,高度: 1000) , ))

center 将允许 ConstrainedBox 的大小不超过屏幕的大小。ConstrainedBox 将把其约束参数中的附加约束强加给它的子约束。

所以 container 必须在 70 到 150 像素之间。它希望拥有 1000 个像素,所以它最终将拥有 150 个像素(最大值)。

例子 12


Center(   child: ConstrainedBox(      constraints: BoxConstraints(         minWidth: 70,         minHeight: 70,         maxWidth: 150,         maxHeight: 150,      ),      child: Container(color: Colors.red, width: 100, height: 100),   ))

center 将允许 ConstrainedBox 的大小不超过屏幕的大小。ConstrainedBox 将把其约束参数中的附加约束强加给它的子约束。

所以 container 必须在 70 到 150 像素之间。它希望有 100 个像素,这就是它的大小,因为这是在 70 和 150 之间。

例子 13


UnconstrainedBox(   child: Container(color: Colors.red, width: 20, height: 50),)

屏幕强制 UnstrainedBox 与屏幕大小完全相同。但是,UnstrainedBox 允许其 Container 子元素具有任意大小。

例子 14


UnconstrainedBox(   child: Container(color: Colors.red, width: 4000, height: 50),);

屏幕强制 UnstrainedBox 的大小与屏幕的大小完全相同,并且 UnstrainedBox 允许其 Container 子节点拥有任何它想要的大小。

不幸的是,在这种情况下,Container 有 4000 像素的宽度,太大以至于无法放入 UnstrainedBox,所以 UnstrainedBox 将显示令人恐惧的“溢出警告”。

例子 15


OverflowBox(   minWidth: 0.0,   minHeight: 0.0,   maxWidth: double.infinity,   maxHeight: double.infinity,   child: Container(color: Colors.red, width: 4000, height: 50),);

该屏幕强制 Overflow Box 与屏幕的大小完全相同,并且 Overflow Box 允许其 Container 子节点拥有任何它想要的大小。

像这样使用 Overflow Box 类似于 UnstrainedBox,不同之处在于,如果子元素不适合这个空间,它将不会显示任何警告。

在这种情况下, container 有 4000 像素的宽度,太大以至于无法放入 Overflow Box,但是 Overflow Box 将简单地显示它所能显示的内容,没有给出任何警告。

例子 16


UnconstrainedBox(   child: Container(      color: Colors.red,      width: double.infinity,      height: 100,   ))

这不会呈现任何内容,您将在控制台中得到一个错误。

UnstrainedBox 允许其子元素具有任意大小,但是其子元素是具有无限大小的 Container。

Flutter 不能呈现无限大小,因此它会抛出一个错误消息: BoxConstraint 强制使用无限宽度。

例子 17


UnconstrainedBox(   child: LimitedBox(      maxWidth: 100,      child: Container(         color: Colors.red,         width: double.infinity,         height: 100,      )   ))

这里不会再出现错误,因为当 LimitedBox 被 UnstrainedBox 赋予无限大小时,它将向其子级传递最大宽度为 100 的值。

注意,如果将 UnstrainedBox 更改为 Center widget ,LimitedBox 将不再应用它的限制(因为它的限制只在获得无限限制时应用) ,并且 container 宽度将允许增长超过 100。

这清楚地说明了 LimitedBox 和 ConstrainedBox 之间的区别。

例子 18


FittedBox(   child: Text('Some Example Text.'),)

屏幕强制 FittedBox 与屏幕大小完全相同。文本将有一些自然宽度(也称为其固有宽度) ,这取决于文本的数量,其字体大小等。

FittedBox 将允许 Text 拥有它想要的任何大小,但是在 Text 告诉 FittedBox 它的大小之后,FittedBox 将缩放它,直到它填满所有可用的宽度。

例子 19


Center(   child: FittedBox(      child: Text('Some Example Text.'),   ))

但是如果我们把 FittedBox 放到 Center 中会发生什么呢?Center 将允许 FittedBox 拥有它想要的任何大小,直到屏幕大小为止。

然后 FittedBox 将自己调整到 Text 的大小,并让 Text 拥有它想要的任何大小。由于 FittedBox 和 Text 具有相同的大小,因此不会发生缩放。

例子 20


Center(   child: FittedBox(      child: Text('This is some very very very large text that is too big to fit a regular screen in a single line.'),   ))

但是,如果 FittedBox 位于 Center 内部,而 Text 太大而不能适应屏幕,会发生什么情况?

FittedBox 将尝试根据文本调整自身大小,但它不能大于屏幕。然后,它将假设屏幕大小,并调整文本的大小,使其适合屏幕。

例子 21


Center(   child: Text('This is some very very very large text that is too big to fit a regular screen in a single line.'),)

但是,如果我们删除 FittedBox,则 Text 将从屏幕获得其最大宽度,并将分隔线,以便它适合屏幕。

例子 22


FittedBox(   child: Container(      height: 20.0,      width: double.infinity,   ))

注意 FittedBox 只能缩放有界(具有非无限宽度和高度)的 widget 。否则,它将不会呈现任何内容,并且您将在控制台中得到一个错误。

例子 23


Row(   children:[      Container(color: Colors.red, child: Text('Hello!')),      Container(color: Colors.green, child: Text('Goodbye!')),   ])

屏幕强制 Row 与屏幕大小完全相同。

就像 UnstrainedBox 一样,Row 不会对其子元素施加任何约束,而是让它们拥有所需的任何大小。然后 Row 将它们并排放置,任何额外的空间将保持为空。

例子 24


Row(   children:[      Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.')),      Container(color: Colors.green, child: Text('Goodbye!')),   ])

由于 Row 不会对其子元素施加任何约束,因此子元素很可能太大而不能适应可用的 Row 宽度。在这种情况下,就像 UnstrainedBox 一样,Row 将显示“溢出警告”。

例子 25


Row(   children:[      Expanded(         child: Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.'))      ),      Container(color: Colors.green, child: Text('Goodbye!')),   ]

)

当一个 Row 子元素包装在一个展开的 widget 中时,Row 将不再让这个子元素定义它自己的宽度。

相反,它将根据其他子级定义展开的宽度,只有这样,展开的 widget 才会强制原始子级具有展开的宽度。

换句话说,一旦您使用了展开,原始子级的宽度将变得无关紧要,并且将被忽略。

例子 26


Row(   children:[      Expanded(         child: Container(color: Colors.red, child: Text(‘This is a very long text that won’t fit the line.’)),      ),      Expanded(         child: Container(color: Colors.green, child: Text(‘Goodbye!’),      ),   ])

如果所有的 Row 子元素都被包装在展开的 widget 中,那么每个展开的 widget 都会有一个与其 flex 参数成比例的大小,只有这样,每个展开的 widget 才会强制其子元素具有展开的宽度。

换句话说,“展开”将忽略其子元素的首选宽度。

例子 27


Row(children:[  Flexible(    child: Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.'))),  Flexible(    child: Container(color: Colors.green, child: Text(‘Goodbye!’))),  ])

如果你使用灵活而不是扩展,唯一的区别是,灵活将让它的 child 有相同或更小的宽度比灵活本身,而扩展强制其 child 有完全相同的宽度扩展。

但是,无论是扩大和灵活将忽略他们的 child 宽度时,自己的尺寸。

注意,这意味着不可能按照 Row 子元素的大小成比例地展开它们。Row 可以使用精确的子元素,也可以在使用扩展元素或灵活元素时完全忽略它。

我的程序包 assorted_layout_widgets 有一个特殊的行 widget ,它可以根据每个子宽度按比例调整单元格的大小。

https://pub.dev/packages/assorted_layout_widgets

例子 28


Scaffold(   body: Container(      color: blue,      child: Column(         children: [            Text('Hello!'),            Text('Goodbye!'),         ]      )))

屏幕强制脚手架与屏幕大小完全相同,因此脚手架填充了整个屏幕。

脚手架告诉 container 它可以是它想要的任何大小,但是不能大于屏幕。

注意: 当一个 widget 告诉它的子部件它可以小于一定的大小时,我们说这个 widget 为它的子部件提供了“松散”约束。稍后再说。

例子 29


Scaffold(   body: SizedBox.expand(      child: Container(         color: blue,         child: Column(            children: [               Text('Hello!'),               Text('Goodbye!'),            ],         ))))

如果我们希望脚手架的子节点与脚手架本身的大小完全相同,我们可以将它的子节点包装到 SizedBox.expt 中。

注意: 当一个 widget 告诉它的子部件它必须具有一定的大小时,我们说这个 widget 为它的子部件提供了“严格的”约束。

紧 × 松约束

常常听到有些约束是“紧”或“松”的,因此了解它的含义是有价值的。

一个严格的约束提供了一个单一的可能性,一个确切的大小。换句话说,紧密约束的最大宽度等于最小宽度,最大高度等于最小高度。

如果你打开 Flutter 的 box.dart 文件并搜索 BoxConstraintsstructors,你会发现:

BoxConstraints.tight(Size size)   : minWidth = size.width,     maxWidth = size.width,     minHeight = size.height,     maxHeight = size.height;

如果您再次访问上面的示例 2,它告诉我们屏幕强制红色 container 与屏幕大小完全相同。当然,屏幕通过向 Container 传递严格的约束来实现这一点。

另一方面,松散约束设置最大宽度/高度,但允许 widget 想多小就多小。换句话说,松散约束的最小宽度/高度都等于零:

BoxConstraints.loose(Size size)   : minWidth = 0.0,     maxWidth = size.width,     minHeight = 0.0,     maxHeight = size.height;

如果您重新访问示例 3,它告诉我们 Center 允许红色 container 更小,但不能大于屏幕。当然,Center 是通过向 Container 传递松散的约束来实现这一点的。最终,Center 的目的是将它从父节点(屏幕)获得的紧密约束转换为其子节点( container )的松散约束。

学习特定 widget 的布局规则

了解一般的布局规则是必要的,但这还不够。

每个 widget 在应用一般规则时都有很大的自由度,所以仅仅通过读取 widget 的名称是无法知道它将做什么的。

如果你试图猜测,你可能会猜错。除非您阅读了 widget 的文档或者研究了它的源代码,否则您无法确切地知道它将如何工作。

布局源代码通常很复杂,所以最好还是阅读文档。但是,如果您决定研究布局源代码,您可以通过使用 IDE 的导航功能轻松地找到它。

这里有一个例子:

  • 在代码中找到一些 Column 并导航到它的源代码(IntelliJ 中的 Ctrl-B)。你会被带到 basic.dart 文件。因为 Column 扩展了 Flex,所以导航到 Flex 源代码(也在 basic.dart 中)。
  • 现在向下滚动,直到找到一个名为 createRenderObject 的方法。如您所见,此方法返回 RenderFlex。这是 Column 的对应呈现对象。现在导航到 RenderFlex 的源代码,它将带您进入 flex.dart 文件。
  • 现在向下滚动,直到找到一个名为 PerformLayout 的方法。这是为 Column 进行布局的方法。

作者的布局包

  • https://pub.dev/packages/align_positioned
  • https://pub.dev/packages/assorted_layout_widgets

如果本文对你有帮助,请转发让更多的朋友阅读。

© 猫哥

  • 微信 ducafecat

  • https://wiki.ducafecat.tech

  • https://video.ducafecat.tech

本文由 mdnice 多平台发布

Flutter 必须知道的布局规则相关推荐

  1. flutter 局部状态和全局状态区别_给 Android 开发者的 Flutter 指南

    这篇文档旨在帮助 Android 开发者利用既有的 Android 知识来通过 Flutter 开发移动应用.如果你了解 Android 框架的基本知识,你就可以使用这篇文档作为 Flutter 开发 ...

  2. 做一个高一致性、高性能的Flutter动态渲染,真的很难么?

    Flutter动态模板渲染架构升级 ​ 最近小组在尝试使用集团DinamicX的DSL,通过下发DSL模板,实现Flutter端的动态化模板渲染.我们解决了性能方面的问题后,又面临了一个新的挑战--渲 ...

  3. 【Flutter】应用开发笔记

    1 获取Flutter SDK 1.下载安装包 2.将压缩包解压,然后把其中的 flutter 目录整个放在你想放置 Flutter SDK 的路径中 勿将 Flutter 安装在需要高权限的文件夹内 ...

  4. 用Flutter实现小Q聊天机器人(二)

    用Flutter实现小Q聊天机器人(一) 用Flutter实现小Q聊天机器人(二) 用Flutter实现小Q聊天机器人(三) 用Flutter实现小Q聊天机器人(四) 用Flutter实现小Q聊天机器 ...

  5. flutter 局部状态和全局状态区别_Android 开发者遇到 5G、AI,写给 Android 开发者的 Flutter 指南

    ​前言 Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK.Flutter 兼容现有的代码,免费并且开源,在全球开发者中广泛被 ...

  6. 必读 | 深入理解 Flutter 的布局约束

    前言 本文最初来自于社区成员 Marcelo Glasberg 在 Medium 发表的文章--"初学者应知应会的 Flutter 高级布局规则 (Flutter: The Advanced ...

  7. Flutter Container属性

    FLUTTER 开发初识 Flutter 布局之Container Container简介 Container 组成 Container的构造 Flutter 布局之Container 最近新接触一个 ...

  8. 为什么说Flutter是革命性的?

    译者按:在本文发布的时候,Flutter 的 SDK 尚不能从中国大陆直接下载,不过我们在和本文作者沟通的时候,他表示 Google 正在考虑为国内开发者提供更便捷的下载方式. Flutter 是什么 ...

  9. Flutter 拨打电话和跳转网页

    首先需要一如库 url_launcher  如下 具体写法如下 import 'package:flutter/material.dart'; import 'package:url_launcher ...

最新文章

  1. celery的使用(最新详细解析)
  2. 离开一线互联网大厂的年轻人在想什么?
  3. Linux初学者的感受
  4. 资源共享冲突问题概述
  5. 超大福利 | 这款免费 Java 在线诊断利器,不用真的会后悔!
  6. 公开课精华 | 无人驾驶中感知的挑战与尝试
  7. 项目SOW工作说明书模板
  8. arduino控制雨滴传感器
  9. 计算机408专业考研真题,2021年计算机考研408历年真题及答案
  10. Win10 远程桌面黑屏问题
  11. 人才培养的金字塔模型
  12. 一种插槽式的组件化框架中间件——SCC
  13. 宝塔下的服务器环境搭建步骤
  14. 【普及组_在线赛】班级聚会(reuntion)
  15. 大学生计算机基础大难,大学生计算机基础实训六样文
  16. java叠加两张png带透明图片
  17. 树莓派烧写OpenWrt系统后外接4G模块实现4G路由即MiFi
  18. C++之initializer_list,可变参数模板参数展开方法
  19. Python Twisted系列教程7:小插曲,Deferred
  20. python批处理原始核磁数据用于DPABI

热门文章

  1. 【WordPress】修改固定链接,文章链接
  2. 浏览器是怎么对html5的离线资源进行加载的呢
  3. 自定义Font Icon
  4. 千锋逆战班学员教你从零基础了解HTML5的知识
  5. Gartner发布中国人工智能软件市场指南,激烈竞争下走向差异化
  6. ARP是怎么工作的?ARP攻击与欺骗又是什么?
  7. 一个蚂蚁曾经的辛酸面试历程
  8. BFS最强—如龙题解
  9. 泊松分布、高斯分布、卡方检验与noise level
  10. 让一个按钮自动触发,自动执行onclick鼠标单击事件. 默认已点击.