初探 Flutter 的 Layout
WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.
Article info
This article is part of:
Categories:
Tags:
Date:
Published: 2021/06/07 - Updated: 2021/07/11
Total: 6050 words
Like
or Dislike
About the Author
很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯
More to explore
延續前一篇的文章:
穿梭在不同的畫面中 - 固定名稱路由法 — 如前文所述,Flutter 有四種 Navigation & Routing 的方式: 我們已經學過了「N1/直接導航法」,現在來看第二種,「固定名稱路由法」。
WriterShelf 思書: 紅寶鐵軌客
我們會從這裡開始修改程式碼,如果你是中途插隊進來的讀者,請參考裡面的程式碼。
按照順序,我們現在應該是要繼續介紹第三種切換畫面的方法:「動態名稱路由法」,但是有一個問題,我們目前開發中的 App 內並沒有動態內容,根本就不能測試,所以我們必須要先建一個動態的內容,才能繼續開發測試。既然如此,那我們就先把「快樂錄音機 Happy recorder」的主介面建立起來吧。
介面設計
要開發 Flutter 的 screen 內容,也就是使用者介面 UI,最重要的就是先要了解 Flutter 的 screen layout(畫面佈局),但是要做 Layout 前,我們必須先有 UI 的設計草圖,在大型開發團隊裡,設計 UI 都會有專人負責,如果是一人團隊,很多人都會偷懶不做,一路改,我的建議是在下手寫程式前,務必要先做個 UI 草圖,不然,相信我,未來一定會讓你改到怕。
我不是設計師,根本沒資格介紹怎麼設計 UI,我只能說我覺得 Figma 不錯用,下面這個 Happy recorder 的 UI 草圖就是用 Figma 做出來的。
我們的「快樂錄音機 Happy recorder」目前的動作還很簡單:
當然,未來還有很多功能要做,例如:新增及修改標題及說明,錄音、暫停,等等⋯⋯ ,別急,我們一步一步來。
我是真覺得 Figma 很簡單好用,它有網頁版不用安裝,又有免費的方案,還可以模擬畫面的動作,對我來說,很夠用了,簡單的用法可以參考下面這個教學,它講的很清楚:
Figma 教學:讓技術開發人員都能輕鬆實作畫面設計 — 全端開發者是否應該具備 UI/UX 設計的技能呢?幸好,UI/UX 設計工具加速了畫面設計 (Screen design) 的開發速度,我將會分享使用 Figma 為我的新 app 設計 UI/UX 的經驗。 AppCoda
很好,我們有 UI 構圖了,接下來就要把它在 Flutter 上「刻」出來。
Flutter Layout Widget
Layout 的英文 = 佈局,就是在畫面上排版,我們現在功力不足,只能用 Flutter 給你的組件 widget 來排版,所以,你要先了解 Flutter 的排版 widget。
一個很重要的觀念,在 Flutter 中的 Layout widget 分成兩大類:
我不是在教你英文,國中有讀畢業的人都知道小孩的單數是 child,複數是 children,我想要講的是在 Flutter 中,Layout widget 一定要使用正確的單複數,要用對 child 及 children,例如:
看到不同點了嗎?
有了單複數 layout widget 的觀念後,再來就是要把 Layout Widgets 想成試一棵「樹」,是的,在 Flutter 中,layout 就是一棵樹,我借用了官網中介紹 Layout 的圖,如下:
這張圖中很清楚的展示了單複數 layout widget 的不同,最上層的 container 只能有一個小孩,但是第二層的 row 可以有很多小孩,這就是 Flutter layout 的主要概念。
這種樹狀的 Layout 方式不是 Flutter 首創,Web 也是這樣,只是在 Web 的開發上,我們都會將網頁內容與樣式分開寫,但是 Flutter 卻是混在一起的,所以程式看起來很雜亂又很長,這也是 Flutter 很讓人詬病的一個問題,我們要想辦法讓它好寫好看一些。
Flutter 中還有一些以 Sliver(銀)開頭的 Widgets,例如:SliverList,這是 layout Widget 中的一個進階版本,Silver 代表的是整個畫面中一小塊可以捲動的區域,所以你可以在畫面中組合不同的 Sliver Widgets 各管各的區域。
好了,就這樣,你已經對 Flutter 的 layout 有概念了,接下來請點開下面兩個 Widgets 的軍火庫網址:
這裡就是 Flutter Widget 軍火庫,各位可以先瀏覽一下就好,試試找找看 text widget 在哪裏?哇,原來 Flutter 有那麼多 widget,layout 也有那麼多種 widget,頭很大,怎麼記得起來,沒關係,常用的不多啦,如下:
標準 widgets
到處都可以用!
Container
: 如果你熟悉 HTML 的 style,這個就很像一個 DOM,它就是一個容器,外面可以加上 padding、margins、borders,也可以設定 background color ......GridView
: 例如可以顯示 3x8 個圖。ListView
: 就是在 App 中很常看到的單行列,我們的 MyHomePage 就要用它。Stack
: 將 widget 疊起來。Material widgets
只能用在 Material Design 中,Material Design 是 Google 開發的一種設計語言,Flutter 也是 Google 創立的,自然 Flutter 也會全力支援 Material design,所以 Flutter 有一大堆 Material widgets,請點開這個 Material Widgets 的網頁看看,這也是為什麼在 Flutter 中,Material design 是最多人用的。最常用的有以下兩個,我們也會用到其中的 Card Widget。
Card
: Material design 的卡片的設計元件。ListTile
: Material design 的三行式的設計元件。需求式學習法:從需求開始學起
我們不可能把所有的 Widget 都背起來,學習就是要從需求開始,我們現在的需求就只要把「快樂錄音機 Happy recorder」的畫面做出來就好。
建立測試資料
我們要做動態名稱,當然要先有動態名稱資料庫,照理,開發程式當然要先做好資料庫分析,看看要怎麼存取,但是我們還在學習,還不會做資料庫,所以就先做一個簡單的測試資料庫。
我們的測試資料格式很簡單,就是一個 AudioRec Class,還記得我們之前談過的「Flutter 程式寫作風格與管理」嗎?沒錯,這個就是屬於資料分類,要放在 models 目錄裡,而其檔案名稱要用小寫底線,我們新增了以下這個程式:
libs/models/audio_rec.dart:
好啦,就這樣,我們的測試資料建好了。
別忘了要 import 到用到的地方,兩個 screen 都要,因為它們都會用到 audioRec。
更新 MyHomePage screen 畫面
我們先從 MyHomePage screen 開始改起,其實仔細一看,就只是把原來的中間 body 的部分改了,如果你之前有乖乖聽話的去喵了一眼 Flutter 的 Widget 列表網頁,你可能就會發現,原來我們要做的 MyHomePage 音檔列表,不過就是一棵小小的 Widget 樹,如下圖:
我已經把每一個 Widget 都附上說明文件連結了,各位可以點開來看看,每一個 Widget 都有範例,有些甚至有影片介紹,所以很容易懂的。
接下來就是修改 my_home_page.dart 的程式了,我們不在需要原來的 _counter 及 _incrementCount() 了,就把它們 remark 或刪除吧,如下圖:
libs/screens/my_home_page.dart:
再來,就是修改其中的 body 部分:
好了,你可以試試看 run 一下,鐺鐺,你應該可以看到下面的畫面出現了:
很不錯,短短的幾行,我們就建立了一個 ListView。在我們繼續下一個 screen 前,我們來介紹一些很重要的資源及工具:
Google Fonts Icons
Flutter 有一個巨大的 icons 軍火庫,藏在 Google Fonts 的 icons 裡,隨時要用,只要在網頁上 Google:「google font icon」,你就可以找到你要的 icon 了,它很好用,連 Flutter 的程式碼都已經寫好等你來用。
Widget 太多層了,好多括弧啊!
Flutter 中的 Widget 包來包去,你剛剛如果不是複製貼上而是自己手打輸入,你就一定會覺得寫的好辛苦,有沒有覺得像掉進括弧的無限迴圈中?沒錯,所以有人稱它是括弧地獄了,這時你就要善用工具了,Android Studio 深知民間疾苦,所以它有特異功能,可以幫你把任一個 Widget 包起來或是移除,秘笈如下:
Flutter devTools
當你在做 Layout 時,別忘了 Flutter 有一個很好用的工具:devTools,它一定要 run 執行程式後,才可以把開啟,它就在 Android Studio Run console 的旁邊,如下圖的中間:
這時瀏覽器上就會自動打開 devTools 的頁面,你應該可以看到以下的圖:
這是一個很新的玩具,把它跟模擬器放在一起,你就可以看到每一個 Widget 的 layout,兩邊都點點玩玩看:
你的畫面可能跟我的圖不一樣,我現在使用的版本是 DevTools 2.2.4,它還在開發中,現在是有些限制,不過未來的功能一定會更強大,這個工具會幫助你進一步的知道每一個 widget 的大小與位置,這很重要,是幫助 layout 抓蟲的利器。
更新 AudioSession screen 畫面
這一個 screen 的程式改變可就大了,我試著依照原先 UI 構圖去做 screen UI,但是遇到很大的挑戰,問題主要是發生在原先 UI 構圖下面的兩個按鈕,我們先來回顧一下原先的 UI 構圖:
我花了兩個小時,試了以下幾種不同的方法,可是都遇到困難,各位目前不需要了解困難在哪裡,我要說的重點是:UI 可以很簡單的畫出來,但是真要用程式刻出來,就要考慮花的時間值不值得了。
表面上,這是我所遇到的問題如下,大家可以快速的瀏覽一下,不需要花時間糾結:
其實真正的問題是:
好啦,我承認,我 UI 構圖天馬行空,亂畫的結果就是浪費很多時間嘗試,還好我是在學習,就當作學到失敗的經驗吧。
這是後來做出來的 UI⋯⋯ 其實我覺得比原來的好看,我想使用者可能根本也感覺不出來差別在哪裡⋯⋯
在公佈程式碼前,各位讀者其實你們已經有足夠的功力與武器了,要不要自己先試試看,自己試試寫程式,把上面的 UI 刻出來,真的不想動手,那至少也要想想,這個 UI 上面到底有那些 Widgets,它是怎麼排列的。
.
.
.
.
好啦,下面就是我的程式碼,因為幾乎全改了,所以就一次全上:
lib/screens/audio_session.dart
好啦,介紹完畢。
啥?就這麼簡單?是的,這總共 112 行的程式中,幾乎都是 Layout 的 Widget,而且各位只要真的很努力、很專心、很細心的看,就會發現不過都是一個包著一個的 widget,可是,說真的,好「難」看,一堆括弧,好啦,各位只是忘記了你們手上的武器了,而且有兩的,一個是 Andriod Studio 的 Flutter Outline,另一個是 devTools,我們都介紹過了,就讓我們打開 devTools 吧。
先看一下 body 的 Layout:
如同我上面介紹的方法,把模擬器跟 devTools 放在一起肩並肩,把「Select Widget Mode」按下,你就可以看到每一個 widget 在哪裏,還有它的爸媽小孩。
上圖中,我們點選了 body 裡的 Cloumn,我們從 devTools 的左邊馬上就可以看到:
我們現在回頭來看程式,是不是清楚易懂多了,有沒有豁然開朗的感覺,基本上,這些 widget 都幾乎不用設定就能用了,我只做了一些排列與大小設定,說明如下:
padding: const EdgeInsets.all(x.0)
,如果你寫過網站,就知道這是跟 DOM 一樣的東西,如下圖,container 可以設定 margin、padding,這也是為什麼要用 Container 的主因,大小設定要用 EdgeInsets:四邊都一樣就是 all(),兩邊一樣就是 symmetric(vertical: x),單邊就是 only(left: x):(幫助記憶:Edge 是邊邊角角 + Inset 是插圖 = 插圖的邊邊角角)好啦,body 講完了,我們來看一下 bottomNavigationBar 的 Layout:
跟上面一樣,把模擬器跟 devTools 放在一起看,我們這次用「怎麼選擇 widget 的角度」來分析,我在上圖中點選了 bottomNavigationBar 裡的 Row,沒特別理由,就只是好介紹而已:
MainAxisAlignment
話說 Flutter 的文件也很奇怪,寫了一大堆關於 MainAxisAlignment 的排列方式,但是就是沒有一個整合的圖例,到底 MainAxisAlignment.XXXX 會怎麼排列,好吧,那就只有辛苦我自己來做一個圖說明了:
左邊就是 MainAxisAlignment.XXXX,右邊就是相對應的排列方式,用圖一點都不難懂,用寫的就像武功秘笈,真搞不懂 Google Flutter team 在想什麼,不過我想也沒人會記得啦,寫程式時,大家都會試試看,反正又不是什麼難事。話又說,CrossAxisAlignment 也很像,不過只有:start/center/end 及 stretch 四種,行為跟 MainAxisAlignment 一樣啦,只有 stretch 不同,它會膨脹填滿,嘿嘿嘿,怎麼膨脹填滿呢?各位有機會試試就知道,反正又不會咬人。
程式中的其他部分應該都很好懂,就算還有我沒提到的,查一下網路應該也很容易找到答案,恭喜各位能堅持到這裡,各位應該也對 Flutter 的 layout 有初步的了解了,大家可以把程式亂改一下,做一些簡單的 UI 變化,反正又不會爆炸咬人,試完後倒退回原來的程式碼就好了,很好玩的。
在結束這篇已經太長的文章前,我還必須要強調一大點,這篇只是開始,要用 Flutter 做一個好的 layout 還有很多事情要考量,例如:
所以做 layout 時,要一直記得:Layout 是給人用的,Layout 會一直被改,Layout 要支援不同的裝置,Layout 要能跟著螢幕轉動變化,Layout 要能出國比賽,⋯⋯
再次恭喜各位已經「認識」Flutter 的 layout 了。
最後的程式碼,請自行參考本書:程式碼備份中的 Code milestone 1。