Flutter Widget 靜態佈局實戰
前面兩篇文章介紹了 Flutter win
環境的安裝,以及利用 listview
實現了簡單佈局。(這篇文章篇幅有點長,讀完大概需要 8.88
分鐘)
Flutter 入門實現 ListView 列表頁面以及收藏頁面
Flutter 環境搭建以及填坑指南(Win10 系統且已有 Android 開發環境這篇文章主要介紹以下內容:
1. ListView 能實現什麼效果?
2. widget 如何新增到 ListView 中?
3. ListView 點選事件,單個 widget 點選事件
4. ( 重點 ) widget 如何垂直、水平擺放?
5. 圖片、Icon、Text widget 的簡單使用
在這篇文章 Flutter 入門實現 ListView 列表頁面以及收藏頁面 中雖然實現了一個列表,但是怎麼實現的還沒仔細研究,現在就先從研究 ListView
的實現開始吧。
先看一下之前實現的效果:

ListView 列表
RandomWordsState
類中的下面程式碼:
return new ListTile( // 單詞佈局 title: new Text( pair.asPascalCase, style: _biggerFont, ), // 喜歡小心心佈局 trailing: new Icon( alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), // ListView item 的點選事件 onTap: () { // 通知框架狀態已經改變 setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, );
從上面的程式碼中可以看出實現這個列表主要就是操作了 ListTile
這個 Widget
,那麼我們看看這個 ListTile
的構造方法都提供了上面功能吧!註釋都寫在程式碼中了~
/// Requires one of its ancestors to be a [Material] widget. const ListTile({ Key key, // 在列表的左邊新增的 widget(如文末圖中的左邊圖片) this.leading, // 標題 this.title, // 副標題 this.subtitle, // 在列表的右邊新增的 widget(如文末圖中的右邊心形 icon) this.trailing, // 如果 isThreeLine 為 true, subtitle 則不能為 null,預設為 false // 如果 副標題為空則列表平鋪一行顯示,如果 副標題 不為空,則副標題所佔的佈局是兩行 // 如果 isThreeLine = true 則副標題可以顯示三行。 this.isThreeLine = false, // bool 型別,預設為false ,如果為 true 則 ListTile 在垂直方向是密集型擺放 //(具體效果待後面去實現,這裡只是看了註釋後的理解) this.dense, // 內容的邊距 this.contentPadding, // item 是否能點選 this.enabled = true, // item 的點選事件 this.onTap, // item 的長按事件 this.onLongPress, // item 是否選中標記 this.selected = false, })
flutter
的開發語言是 dart
,這個語言之前沒學過,這裡就先不深入學習了,網上有比較好的這個博主戀貓月亮
寫的系列的文章寫的很好。我這裡就只寫實現上面圖片中展示的效果。
1. ListView 能實現什麼效果?
ListView 不僅可以實現列表佈局,還可以實現 Android 中的 ScrollView 的功能。關於 滾動的 widget 還有其他的實現,可參考 可滾動Widget簡介
實現列表功能,如果是符合 ListTile
的樣式,直接使用 ListTile
實現很方便。(目前還沒學到如何實現多佈局,後面學習再寫一篇)
實現 ScrollView
功能,直接在 build
方法的 body
中新增我們的 widget
。虛擬碼如下:
定義了一個 DetailScreen
詳情頁面,繼承了 StatelessWidget
,在 build
方法中返回了頁面的內容: appBar
是標題欄, body
是顯示的內容
class DetailScreen extends StatelessWidget { // Declare a field that holds the pair final WordPair pair; // In the constructor, require a pair DetailScreen({Key key, @required this.pair}) : super(key: key); @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( // 接收傳遞過來的單詞做標題名字 title: new Text("${pair.asPascalCase}"), ), // 使用 ListView 做滾動列表 body: new ListView( children: [ // 顯示網路圖片 new Image.asset( 'images/wali.jpg', width: 600.0, height: 240.0, fit: BoxFit.cover, ), // 標題行(文末有實現效果) titleSection, // 按鈕行(文末有實現效果) buttonSection, // 描述文字(文末有實現效果) textSection, ], ), ); } }
2. widget 如何新增到 ListView 中?
上面的虛擬碼中在構建一個有狀態的 widget
的時候會重寫 build
方法,在該方法中的 body
返回我們實現的 widget
即可。
3. ListView 點選事件,單個 widget 點選事件
如果 ListView
使用 ListTile
實現列表的話,直接使用 ListTile
中的 onTap
實現列表點選效果。如果不是用 ListTile
實現,那就使用單個 widget
的點選事件吧。
widget
的如果有 onTap
方法可以直接呼叫該方法即可實現,如果沒有該方法,則需要使用 GestureDetector
來實現點選效果,例如上圖中的心形喜歡點選事件,虛擬碼實現如下:
關於 GestureDetector
可參考該文章 手勢識別GestureDetector
trailing: new GestureDetector( // 心形喜歡 icon child: new Icon( alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), // 點選事件 onTap: (){ // 通知框架狀態已經改變 setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, ),
4. widget 如何垂直、水平擺放?
最常見的佈局模式之一是垂直或水平排列widget。在 Flutter
可以使用行 Row
水平排列 widget
,並使用列 Column
垂直排列 widget
。 同時,每個孩子本身可以是一個 Row
或一個 Column
,依此類推。以下示例顯示如何在行或列內巢狀行或列。
通過下面兩張圖片可以學到在實現佈局時如何拆分:
參考連結 widget 佈局

左側的一列和右側的圖片

左側的Column widget樹巢狀行和列
在控制行或列對齊其子項時使用 mainAxisAlignment
( 主軸 ) 和 crossAxisAlignment
( 橫軸 ) 屬性來。 對於行 (Row)
來說, 主軸是水平方向,橫軸垂直方向。 對於列 (Column)
來說, 主軸垂直方向,橫軸水平方向。

Row

Column
MainAxisAlignment 和 CrossAxisAlignment 類提供了很多控制對齊的常量.
MainAxisAlignment-
center → const MainAxisAlignment :子
widget
在主軸方向上居中顯示 -
end → const MainAxisAlignment :子
widget
在主軸方向上居右邊顯示,如果是水平方向,那麼由 TextDirection 來決定end
是在左邊(ltr)還是在右邊(rtl)。如果是豎直方向,那麼由 VerticalDirection 來決定end
是在上邊(up)還是在下邊(down) -
start → const MainAxisAlignment :子
widget
在主軸方向上居左邊顯示,同理end
。 -
spaceAround → const MainAxisAlignment :子
widget
中的第一個和最後一個widget
距離邊的距離是它與中間的距離的一半。(這裡比較抽象,後期實現效果了會順帶講解) -
spaceBetween → const MainAxisAlignment :子
widget
中的第一個和最後一個貼邊,剩餘的子widget
將中間的空間平分。 -
spaceEvenly → const MainAxisAlignment :子
widget
將佈局空間完全平分Row 方向完全平分
-
values → const List <<wbr> MainAxisAlignment >:根據值得大小來分配空間,值越大空間分配越多。
CrossAxisAlignment屬性相同的基本和上面介紹的一樣,就不贅述了。
- baseline → const CrossAxisAlignment :基線對齊
- center → const CrossAxisAlignment
- end → const CrossAxisAlignment
- start → const CrossAxisAlignment
- stretch → const CrossAxisAlignment :子
widget
填充橫軸(CrossAxisAlignment),如果橫軸上子widget
很多,會使橫軸變得很擠。 - values → const List <<wbr> CrossAxisAlignment >
Expanded widget
Expanded widget,可以將widget的大小設定為適和行或列 Expanded
有個屬性 flex(彈性係數)
,預設情況下,每個widget的彈性係數為1,也就是會鋪滿布局。
例如實現:三張圖片水平鋪滿螢幕,即可使用 Expanded
包裹 Image
然後設定 flex:1
,不設定也行,因為預設 flex
就是 1

效果圖
虛擬碼如下:
body: new Center( child: new Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ new Expanded( child: new Image.asset('images/pic1.jpg'), ), new Expanded( child: new Image.asset('images/pic2.jpg'), ), new Expanded( child: new Image.asset('images/pic3.jpg'), ),
聚集 widgets
預設情況下,行或列沿著其主軸會盡可能佔用儘可能多的空間,但如果要將孩子緊密聚集在一起,可以將 mainAxisSize
設定為 MainAxisSize.min
。
例如實現:五個星形圖示圖示緊湊在一起。

效果圖
虛擬碼如下:
class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { var packedRow = new Row( mainAxisSize: MainAxisSize.min, children: [ new Icon(Icons.star, color: Colors.green[500]), new Icon(Icons.star, color: Colors.green[500]), new Icon(Icons.star, color: Colors.green[500]), new Icon(Icons.star, color: Colors.black), new Icon(Icons.star, color: Colors.black), ], ); // ... }
5. 圖片、Icon、Text widget 的簡單使用
這些都是基礎控制元件使用起來很簡單的,可檢視這個文件介紹: 基礎 Widget
在學習完上面的內容我們可以實現出目前的樣式了:

flutter_1.gif
實現列表功能,以及一個可滾動的詳情頁面,頂部的標題是 listview 傳遞過去的單詞。
看原始碼或者上文中不理解的可以聯絡我,我會知無不言的。
本文完。