关于程序员:Flutter-必须知道的布局规则

57次阅读

共计 9704 个字符,预计需要花费 25 分钟才能阅读完成。

Flutter 必须晓得的布局规定

很多人说布局不纯熟,我倡议是先去理解布局规定,我之前的文章有讲过。

明天看到一文写的很好,我做了翻译,工夫关系没有润色太多,而后我也筹备了视频做理解说,心愿能帮到你。

原文 https://rohanjariwala03.mediu…

前言

当学习 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/…

例子 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/asso…

例子 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/alig…
  • https://pub.dev/packages/asso…

如果本文对你有帮忙,请转发让更多的敌人浏览。

© 猫哥

  • 微信 ducafecat
  • https://wiki.ducafecat.tech
  • https://video.ducafecat.tech

本文由 mdnice 多平台公布

正文完
 0