回到昨天 StatefulWidget
的 build
會先看到 Scaffold
這個 widget ,這是一個Material 所提供的一個基本的排版widget,通常都會當作一個頁面的開始,這裡有用上三個參數 appBar
、 body
、 floatingActionButton
。
appBar
就是app最上方的工具列, 這裡是用 AppBar
這個widget ,主要就是一些頁面標題或者操作選單或回到上一頁按鈕會出現在這裡,appBar
的型別是 PreferredSizeWidget
,所以其實只要是PreferredSizeWidget
都可以傳進 appBar
也不一定要用AppBar
就是了。
然後是body
就是主要的顯示區塊,通常都會放置我們主要要操作及顯示的Widgets。
floatingActionButton
則是那顆懸浮按鈕通常會是那頁最主要的操作,像是google導航的導航按鈕就是做在這顆按鈕。而他的位置預設會是右下角也可以使用其他參數來控制他的位置。
而body
裡的 widget其實我們光是看命名也很好猜出他們的功能是什麼, Center
就是置中用、 Column
就是一個直行的容器, Text
就是放文字的。這裡就能體會到 aggressive composability 的好處就是當每個功能/排版/容器都是一個widget時我們可以從命名就能知道他的作用是什麼,而缺點是等你開始組成一個複雜的layout你會發現這個深度會令人無法直視。
就 flutter 官方上的 widget 列表的數量來說一個一個介紹顯然不切實際,所以大概就只會簡介一下常用的widget。
通常我在找我想用的widget時如果是沒有比較複雜互動的widget的話,我傾向先去我 catalog 來查找,當然如果真的很複雜的功能就會去找第三方套件之類的。
所以我們來看一下這個 catalog 有哪些常用的widget。
基本上最最最常用的還是 Layout
這個分類的widget,在這個分類下有分成這三種widget。
接受widget的參數是 child
也就是只能傳入單一個 widget 的 widget,像是 Container
、SizedBox
、Expanded
、 Padding
等等。
接受widget的參數是 children
所以就是能傳入 List<Widget>
,這類的有 Row
、 Column
、Stack
、 ListView
、 GirdView
等等。
可以自定義滾動效果的widget,但這部分我就沒有實作就不額外說明了。
而另外一個最常用的就是 Container
,基本上是可以想像成沒有很自由的 div
在使用,大部分是拿來當作一個元件的基底widget,可以設定固定寬高及padding,可以使用 decoration
做額外的樣式等等設定。
Container(
width: 300,
height: 300,
child: Text('這是一個高寬都是300px的Container'),
decoration: BoxDecoration(color: Colors.black26),
),
在Flutter中的Row
、 Column
都是flex的所以如果有寫過網頁前端的讀者應該能蠻容易理解Row、Column的排版邏輯。
基本上跟css中的flexBox的概念一樣,會有兩條軸與主軸方向平行一致就是Main Axis與之垂直就是Cross Axis。
所以在預設專案的這段中
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
Column
的 mainAxisAlignment
的意思是指在主軸上的對齊模式,所以以上面例子來看就是:對在主軸方向置中,也就是從畫面上是垂直置中。
MainAxisAlignment
總共有幾個值
MainAxisAlignment.start: 盡可能將children
向主軸起點放置
MainAxisAlignment.end: 盡可能將children
向主軸終點放置
MainAxisAlignment.center: 盡可能將children
向主軸中間放置
MainAxisAlignment.spaceBetween: 將剩餘間距分散在各元素之間但第一個元素的前面及最後一個沒有間距。
MainAxisAlignment.spaceAround: 將剩餘間距分散在各元素之間但第一個元素的前面及最後一個沒只有一半的間距。
MainAxisAlignment.spaceEvenly: 將間距平均分散在每個元素之間包含第一個元素的前面及最後一個元素後面
使用 Row
來排列的話,這三者的差異在畫面上的差異是這樣:
CrossAxisAlignment
則是垂直軸的排列方式:
CrossAxisAlignment.start:元素的開始位置是在垂直軸的起點
CrossAxisAlignment.end:元素的開始位置是在垂直軸的終點
CrossAxisAlignment.center:元素的開始位置是在垂直軸的中間
CrossAxisAlignment.stretch:將元素佔滿垂直軸
CrossAxisAlignment.baseline:將元素按照垂直軸的基準線排列(不常用)
以 Row
來說 CrossAxisAlignment.start 就是元素會貼著上面,CrossAxisAlignment.center就會是垂直置中。
Row及Column的要注意的一點是 「不能換行」 ,他們就是單純的一條線,如果超出外面容器的寬度就會直接跳出錯誤。
這邊是一個 Container
包著 Column
再包 Row
Container(
width: 300,
child: Column(
children: [
Text('這是一個寬度300px的Container'),
Row(
children: [
...List.generate(
5,
(_) => const Text('123456456'),
)
],
)
],
),
decoration: BoxDecoration(color: Colors.black26),
),
以這個例子來說通常是會將 Row
換成 Wrap
來達成換行的需求。
Container(
width: 300,
child: Column(
children: [
Text('這是一個寬度300px的Container'),
Wrap(
children: [
...List.generate(
5,
(_) => const Text('123456456'),
)
],
)
],
),
decoration: BoxDecoration(color: Colors.black26),
),
我個人是覺得會使用 Container
、Row
及 Column
就能完成一些很基礎的排版需求了,剩下的等實際開發時遇到再來說明。
明天就來做我們第一個小專案也順便開始真正的運用狀態管理。