iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
3
Mobile Development

30天手滑用Google Flutter解鎖Hybrid App成就系列 第 5

30天Flutter手滑系列 - 基本組件(Basic Widgets)

誠如先前文章所說,Flutter由許多Widgets去組成整個App,從這章節開始會從UI相關的Widgets開始介紹,
順便了解不同Widgets的名稱在Mobile development中的代表的是什麼組件。

基本組件(Basic widgets)

在建構一個App,一定會用到的就是Basic widgets,下面會逐一介紹在這個Widget內有哪些類別可以使用。

1. AppBar

https://ithelp.ithome.com.tw/upload/images/20190912/20120028VrSJ1awLxI.png

App bar可由Toobar或是其他Widgets組成,像是TabBarFlexibleSpaceBar,如圖所見,這是一個常駐於螢幕上方的工具列。
App bar最常見被使用在Scaffold.appBar這個屬性。在這個widget內,預設會提供一個固定高度的app bar在螢幕上方的位置。

AppBar包含許多屬性,有leadingtitleactionsbottomflexibleSpace
在下圖列出AppBar這個Widget每個插槽的相對位置,排列方式是由左至右。
https://ithelp.ithome.com.tw/upload/images/20190912/20120028XwgriiWg5H.png


2. Column

https://ithelp.ithome.com.tw/upload/images/20190912/20120028tboUf6XvAv.png

如圖所示,這是用來將子組件排列成垂直方向的widget,若希望它會自動填滿剩餘空間,需要將其包裝在Expanded 內。
另外,column並沒有有捲動(scroll)效果,如果需要能夠捲動,可以考慮使用ListView

下方範例示範將widgets垂直排列,並擴展最後一個logo。
https://ithelp.ithome.com.tw/upload/images/20190912/20120028MYyS9BDSqF.png

Column(
  children: <Widget>[
    Text('Deliver features faster'),
    Text('Craft beautiful UIs'),
    Expanded(
      child: FittedBox(
        fit: BoxFit.contain, // otherwise the logo will be tiny
        child: const FlutterLogo(),
      ),
    ),
  ],
)

3. Container

https://ithelp.ithome.com.tw/upload/images/20190912/20120028LYeDNw3KP6.png

這個widget能組合painting、positioning和sizing這些widget,就是常見的容易概念。
首先,放在這個容器內的子組件會先計算出padding,然後套用其被約束(constraints)的寬高,最後
才是計算margin的大小。

以下範例是在Center這個widget內放入一個container,並且描述它的寬高及margin。
https://ithelp.ithome.com.tw/upload/images/20190912/20120028iPa8HNOThj.png

Center(
  child: Container(
    margin: const EdgeInsets.all(10.0),
    color: Colors.amber[600],
    width: 48.0,
    height: 48.0,
  ),
)

4. FlutterLogo

https://ithelp.ithome.com.tw/upload/images/20190912/20120028FTwwn3SPYT.png

這個Widget有點奇妙,就是召喚一個Flutter logo出來。可以想到的就是在培訓或是作為demo的產品時可以使用。


5. Icon

https://ithelp.ithome.com.tw/upload/images/20190912/20120028Szs01AeDbE.png

在Flutter內,已經內建豐富的icons可供使用。

用法可以參考如下:

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: const <Widget>[
    Icon(
      Icons.favorite,
      color: Colors.pink,
      size: 24.0,
      semanticLabel: 'Text to announce in accessibility modes',
    ),
    Icon(
      Icons.audiotrack,
      color: Colors.green,
      size: 30.0,
    ),
    Icon(
      Icons.beach_access,
      color: Colors.blue,
      size: 36.0,
    ),
  ],
)

6. Image

https://ithelp.ithome.com.tw/upload/images/20190912/201200280GzqMAZqAm.png

用來顯示圖片的Widget,支援格式有:JPEG、PNG、GIF、Animated GIF、WebP、Animated WebP、BMP和WBMP。
圖片可以被多種方式引入:

以下範例是透過其中一種ImageProvider獲得圖片:

const Image(
  image: NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
)

7. Placeholder

https://ithelp.ithome.com.tw/upload/images/20190912/20120028z1ONVBI4D9.png

這個Widget會繪製一個Box,用來表示這個地方未實作完成,未來有可能放入別的widget在這個位置上。


8. RaisedButton

https://ithelp.ithome.com.tw/upload/images/20190912/20120028pKdQjiIzCU.png

這是一個基於Material Design的凸起按鈕。

按鈕本身帶有onPressed這個callback,若這個callback為null,即為disabled的效果。

以下範例分別示範disabled RaisedButton、enabled RaisedButton和漸層(gradient)背景色:
https://ithelp.ithome.com.tw/upload/images/20190912/20120028NblyIAMMIx.png

Widget build(BuildContext context) {
  return Center(
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        const RaisedButton(
          onPressed: null,
          child: Text(
            'Disabled Button',
            style: TextStyle(fontSize: 20)
          ),
        ),
        const SizedBox(height: 30),
        RaisedButton(
          onPressed: () {},
          child: const Text(
            'Enabled Button',
            style: TextStyle(fontSize: 20)
          ),
        ),
        const SizedBox(height: 30),
        RaisedButton(
          onPressed: () {},
          textColor: Colors.white,
          padding: const EdgeInsets.all(0.0),
          child: Container(
            decoration: const BoxDecoration(
              gradient: LinearGradient(
                colors: <Color>[
                  Color(0xFF0D47A1),
                  Color(0xFF1976D2),
                  Color(0xFF42A5F5),
                ],
              ),
            ),
            padding: const EdgeInsets.all(10.0),
            child: const Text(
              'Gradient Button',
              style: TextStyle(fontSize: 20)
            ),
          ),
        ),
      ],
    ),
  );
}

9. Row

https://ithelp.ithome.com.tw/upload/images/20190912/20120028n6Rvpc1aHp.png

相對於column的垂直排列,row就是讓子組件能夠水平排列。
用法也很類似,一樣有個Expanded widget可以填滿水平空間。
同樣row也是不能捲動(scroll),若有捲動需求還是得參考ListView

下方範例示範水平排列並且填滿可用空間。
https://ithelp.ithome.com.tw/upload/images/20190912/20120028XlhekdjW2J.png

Row(
  children: <Widget>[
    Expanded(
      child: Text('Deliver features faster', textAlign: TextAlign.center),
    ),
    Expanded(
      child: Text('Craft beautiful UIs', textAlign: TextAlign.center),
    ),
    Expanded(
      child: FittedBox(
        fit: BoxFit.contain, // otherwise the logo will be tiny
        child: const FlutterLogo(),
      ),
    ),
  ],
)

10. Scaffold

https://ithelp.ithome.com.tw/upload/images/20190912/20120028LSrYuaBfk9.png

在前一天的文章裡有稍微提到這個widget,我的認知這是個比較跟佈局有關的widget。
這個class針對drawers、snack bars和bottom sheets提供了多種APIs。

在這個展示的範例,Scaffold內有bodyFloatingActionButton兩個屬性。在Body內有一個Text Widget被包在Center內,然後FloatingActionButton連動一個callback去進行計數的動作。
https://ithelp.ithome.com.tw/upload/images/20190913/20120028nkHPkQGLKZ.png

int _count = 0;

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Sample Code'),
    ),
    body: Center(
      child: Text('You have pressed the button $_count times.')
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () => setState(() => _count++),
      tooltip: 'Increment Counter',
      child: const Icon(Icons.add),
    ),
  );
}

11. Text

https://ithelp.ithome.com.tw/upload/images/20190912/20120028iKsBeMedIl.png

用來顯示單一樣式的文字,文字斷點的位置取決於佈局的限制。

下方範例針對超過限制的文字,改以...顯示:
https://ithelp.ithome.com.tw/upload/images/20190913/20120028uN3UBzMCnr.png

Text(
  'Hello, $_name! How are you?',
  textAlign: TextAlign.center,
  overflow: TextOverflow.ellipsis,
  style: TextStyle(fontWeight: FontWeight.bold),
)

若要顯示一段文字,並且設定為不同樣式時,可以用Text.rich
https://ithelp.ithome.com.tw/upload/images/20190913/201200287IkEyYsz7Y.png

const Text.rich(
  TextSpan(
    text: 'Hello', // default text style
    children: <TextSpan>[
      TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
      TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
    ],
  ),
)

總結

到這裡,應該可以體會Flutter是由很多個Widgets組成,光是basics widget就有這麼多,但熟悉了這些,要套用其他分類的widgets就快很多了。
明天會繼續介紹其他常見的UI Widgets,

然後祝大家中秋節快樂


參考資料

https://flutter.dev/docs/development/ui/widgets/basics


上一篇
30天Flutter手滑系列 - Dart語言基礎教學
下一篇
30天Flutter手滑系列 - 文字(Text Widgets)、圖片相關組件(Assets, Images, Icon Widgets)與文字輸入(Input Widgets)
系列文
30天手滑用Google Flutter解鎖Hybrid App成就30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Austin7L
iT邦新手 5 級 ‧ 2021-10-05 14:02:02

第2點 children: <Widget>前面是不是要加上 const?
初學照做,vscode會出現藍波,解決方法加上const後就正常了,
再請大師指點,謝謝。

Column(
  children: const <Widget>[
    Text('Deliver features faster'),
    Text('Craft beautiful UIs'),
    Expanded(
      child: FittedBox(
        fit: BoxFit.contain, // otherwise the logo will be tiny
        child: const FlutterLogo(),
      ),
    ),
  ],
)

我要留言

立即登入留言