本問如今是官方文檔的一部分了
當Fluter初學者問你爲何組件裏的width:100
不是100像素的時候,默認的答案就是告訴他們把組件放進一個Center
裏,對吧?html
不要這麼幹。git
若是你這麼幹了,他們會一次一次的問你爲何FittedBox
有問題,爲何Column
會overflow,又或者IntrinsicWidth
是作什麼的。github
因此,一開始就告訴他們Flutter的佈局和html有很大的不一樣,他們極可能就是html的高手,而後讓他們記住如下的規則:函數
不理解這個規則,Flutter的佈局是無法弄清楚的。因此,我(做者)以爲最好今早的學會它。佈局
細節:學習
好比一個Column組件,已經設定了padding
值,如今要給它的兩個子組件設定佈局:字體
組件 -- 詢問父組件約束是啥。
父組件 -- 你只能是90
到300
寬,30
到85
高。
組件 -- 嗯~~ 我還要5個單位的padding,那麼個人子組件只能有最大290
的寬和75
的高。
組件 -- 嗨,第一個子組件你必須是0
~290
寬,0
~75
高。
第一個子組件 -- 我要290
寬,20
高。
組件 -- 嗯~~ , 既然我要把第二個子組件放在第一個的下面,這樣就剩下55
的高度給第二個子組件了。
組件 -- 嗨,第二個子組件你必須是0
~290
寬,0
~55
高。
第二個子組件 -- 好的,我要140
寬和30
高。
組件 -- 很好,我會把第一個子組件放在x軸:5
,y軸:5
,第二個子組件x軸:80
,y軸:25
的位置。
組件 -- 嗨,父組件。個人size是300
寬,60
高。
Flutter佈局引擎在上面規則的基礎上還有一些其餘的限制:flex
下面是一個互動示例。spa
原文提到了CodePen,也能夠在如下兩種方法裏選一種。
Container(color: Colors.red);
屏幕是Container
的父組件,它會把紅色的Container
嚴絲合縫的約束在整個的屏幕內部。設計
因此,Container
填滿了整個屏幕,處處都是紅色。
Container(color: Colors.red, width: 100, height: 100)
Container
想要寬100,高100,可是不行。屏幕會強制它填滿屏幕。
因此Container
填滿了屏幕。
屏幕強制Center
填滿整個屏幕,因此Center
顯示在全屏。
Center
告訴Container
能夠擁有想要的大小,可是不能比屏幕還大。因此,Container
的大小就是100x100。
Align( alignment: Alignment.bottomRight, child: Container(width: 100, height: 100, color: Colors.red) )
這和前一個例子並不同,這裏用的是Align
而不是Center
。
Align
也會告訴Container
能夠任意大小,可是若是有任意的可用空間,是不讓Container
居中的,它會把Contaienr
放在右下角。
Center( child: Container( color: Colors.red, width: double.infinity, height: double.infinity, ) )
屏幕強制Center
填充整個屏幕,因此Center
填滿了屏幕。
Center
告訴Container
能夠是任意大小,Container
要的是無限大,可是它又不能比屏幕還大,因此也填滿了屏幕。
Center(child: Container(color: Colors.red))
屏幕仍是會強制Center
填充屏幕。
Center
會告訴Container
能夠爲任意大小,可是不能比屏幕大。由於Container
沒有子組件,也沒有固定的大小。它會決定顯示爲儘量的大,因此填充了屏幕。
可是,爲何Container
要這麼決定呢?這是設計決定的。因此,Container
在這樣的狀況下會如何顯示,你要查看文檔。
Center( child: Container( color: Colors.red, child: Container(color: Colors.green, width: 30, height: 30), ) )
Center
會填充屏幕。
Center
會告訴Container
能夠任意大小。Container
沒有大小,可是有一個子組件,因此它決定和它的子組件同樣大小。
紅色的Container
告訴它的子組件能夠爲任意大小,可是不能比屏幕還大。
綠色的Container
想要30 x 30。就像上面說的,紅色的Container
就會顯示爲綠色的Container
的大小,也是30x30。沒有紅色能夠顯示出來,由於綠色的把紅色所有覆蓋住了。
Center( child: Container( color: Colors.red, padding: const EdgeInsets.all(20.0), child: Container(color: Colors.green, width: 30, height: 30), ) )
紅色的Container
會顯示爲其子組件的大小,可是它本身還有padding因此它自己的大小是70x70(=30x30 + 20的padding值)。最後紅色由於有padding值是可見的,綠色Container
和上例同樣有30x30的大小。
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
顯示到屏幕的大小,因此constaints
參數的值都被忽略了。
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
會讓它的子組件使用額外的約束,並把這個約束做爲constraints
參數傳入子組件。
因此Container
必須在70到150之間,Container
雖然設定爲10的大小,可是最後仍是顯示爲70(最小值)。
Center( child: ConstrainedBox( constraints: BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ), child: Container(color: Colors.red, width: 1000, height: 1000), ) )
Center
容許ConstraintedBox
是屏幕內的任意大小。ConstrainedBox
會把它的額外約束經過constraints
參數傳入給它的子組件。
因此,Container
必須是在70到150之間。它想要設定爲1000,因此最後的值爲150(最大值)。
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之間的。
UnconstrainedBox( child: Container(color: Colors.red, width: 20, height: 50), )
屏幕強制UnconstrainedBox
擁有和屏幕同樣的大小。而UnconstrainedBox
容許它的子組件有任意大小。
UnconstrainedBox( child: Container(color: Colors.red, width: 4000, height: 50), );
屏幕強制UnconstrainedBox
和屏幕同樣大小,而UnconstrainedBox
讓它的Container
子組件擁有任意大小。
可是,本例中Container
設定的是4000的寬,這樣太大了無法放進UnconstrainedBox
,因此UnconstrainedBox
會顯示出「overflow warning」。
OverflowBox( minWidth: 0.0, minHeight: 0.0, maxWidth: double.infinity, maxHeight: double.infinity, child: Container(color: Colors.red, width: 4000, height: 50), );
屏幕強制OverflowBox
和屏幕一個大小,而且OverflowBox
讓它的子組件Container
能夠有任意大小。
OverflowBox
和UnconstrainedBox
相似,不一樣的地方是,若是子組件比它大的話不會包warning。
在本例中Container
的寬是4000,太大了。可是OverflowBox
在這裏就不會像上例的UnconstrainedBox
同樣報警。
UnconstrainedBox( child: Container( color: Colors.red, width: double.infinity, height: 100, ) )
它不會繪製出任何的東西,只會在console裏報錯。
UnconstrainedBox
讓它的子組件能夠擁有任意大小,然而它的子組件的寬是double.infinity
。
Flutter無法繪製無限寬的大小,因此它會拋出一個錯誤:BoxConstraints forces an infinite width。
UnconstrainedBox( child: LimitedBox( maxWidth: 100, child: Container( color: Colors.red, width: double.infinity, height: 100, ) ) )
FittedBox( child: Text('Some Example Text.'), )
屏幕強制FittedBox
和屏幕一樣大小。Text
會有本身的寬度(也叫作intrinsic寬度)。這個值依賴於字體和文字的多少等。
FittedBox
會讓Text
擁有任意的大小,可是Text
把它本身的大小通知FittedBox
以後,FittedBox
會作縮放,直到填滿整個的寬度。
Center( child: FittedBox( child: Text('Some Example Text.'), ) )
可是,若是把FittedBox
放在Center
裏面的話會發生什麼呢?Center
會讓FittedBox
擁有任意它想要的大小。
FittedBox
而後會把本身的大小縮放到Text
的大小。由於FittedBox
和Text
有一樣的大小,因此就不會有縮放的發生了。
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
裏,並且文字內容多到屏幕放不下的時候會發生什麼呢?
FittedBox
會縮放到適應Text
的大小,可是它不可能比屏幕還大。它會首先佔用屏幕的大小,而後對Text
縮放到能夠顯示在屏幕裏。
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
就會使用屏幕的寬度,而後折行來適應這個寬度。
FittedBox( child: Container( height: 20.0, width: double.infinity, ) )
注意FittedBox
只能縮放一個有邊界的組件(寬度或者高度都沒有無限值)。不然它不會繪任何的東西,只會在console裏顯示一條報錯信息。
Row( children:[ Container(color: Colors.red, child: Text('Hello!')), Container(color: Colors.green, child: Text('Goodbye!)), ] )
屏幕會強制Row
使用屏幕的寬度。
就和UnconstrainedBox
同樣,Row
也不會對它的子組件施加任何的約束,而是容許他們有他們想要的任意大小。Row
會把他們挨個放好,而後空出剩餘的空間。
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
的寬度。這時候,就和UnconstrainedBox
同樣顯示錯誤信息「overflow warning」。
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
的子組件被Expanded
包裹的話,那麼這個組件的寬度就不會再起做用了。它會使用Expanded
的寬度。Expanded
則強制原先的子組件使用Expanded
的寬度。
總之一句話,只要你用了Expanded
,那麼它子組件的寬度就無效了。
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
組件的全部子組件都由Expanded
包裹,那麼每一個子組件所佔的比例是由flex
參數決定的。每一個子組件租後都會接受Expanded
的寬度。
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!’))), ] )
本例惟一不一樣的就是使用Flexible
代替了Expanded
。Flexible
會讓它的子組件有更小的寬度嗎?
Flexible
和Expanded
都會忽略子組件的大小。
也就是說在Row
裏不可能根據子組件的大小按比例縮放子組件。Row
要麼使用子組件的寬,要麼在你使用了Expanded
或者Flexible
的時候徹底忽略他們的寬度。
屏幕強制Scaffold
顯示到屏幕的大小。因此,Scaffold
填充了屏幕。
Scaffold
告訴Container
它可使屏幕內的任意大小。
注意:當一個組件告訴它的子組件比某個特定的值小,咱們能夠認爲這個組件給它的子組件提供了「鬆散」的約束。後面會有更詳細講解。
Scaffold( body: **SizedBox.expand**( child: Container( color: blue, child: Column( children: \[ Text('Hello!'), Text('Goodbye!'), ], ))))
若是咱們要Scaffold
的子組件和它有相同的大小。咱們能夠把它的子組件放到SizedBox.expand
。
注意:當一個組件告訴它的子組件必須是某個特定的值,咱們能夠說這個組件給它的子組件提供了「緊」約束。
常常會聽到一種說法「緊」或者「鬆散」的約束,那麼他們到底指的是什麼呢?
一個緊約束只提供了一種可能,一個準確的值。也就是說,一個緊約束,它的最大寬和最小寬是同樣的,最大高和最小高也是同樣的。
若是你到Flutter的box.dart
文件查找BoxConstraints
構造函數,你會發現:
BoxConstraints.tight(Size size) : minWidth = size.width, maxWidth = size.width, minHeight = size.height, maxHeight = size.height;
若是你再看示例 2,屏幕強制紅色的Container
顯示到屏幕的大小。屏幕能夠這麼作就是給Container
傳入了緊約束。
一個鬆散的約束是指給子組件設定最大寬和高,可是讓子組件儘量的小。也就是說,一個鬆散約束有最小的寬和高,他們都等於0.
BoxConstraints.loose(Size size) : minWidth = 0.0, maxWidth = size.width, minHeight = 0.0, maxHeight = size.height;
早示例 3中,Center
讓紅色的Container
不要比屏幕還大。Center
就是給Container
傳入了一個鬆散的約束。最後的效果是Center
收到了父組件傳給它的緊約束,而給它的子組件Container
傳入了鬆散約束。
知道了基本的佈局規則,也仍是不夠的。
每一個組件都有高度的自由決定如何使用基本佈局規則,因此若是隻是看到了組件名稱是沒法預測這個組件的佈局行爲的。
若是你去猜,基本都會錯。你不讀文檔或者組件的源代碼是沒法準確知道一個組件如何佈局的。
源代碼基本都很複雜,因此最好仍是看文檔。固然若是你想學習佈局的相關代碼,IDE的支持是足夠支持你的想法的。
這裏有個例子:
Column
,而後跳轉到它的源碼。最終你會到basic.dart
文件。Column
繼承自Flex
,還能夠跳轉到Flex
,它也在basic.dart
文件裏。createRenderObject
的方法。這個方法返回RenderFlex
。這就是Column
對應的繪製對象。再跳轉到RenderFlex
的源碼,你就會到flex.dart
文件。的方法,這個方法就是爲
Column`佈局的方法。很是感謝Simon Lightfoot的校驗,文章開頭的圖片和對文章內容的建議。