iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Mobile Development

Flutter 30: from start to store系列 第 11

Flutter介紹:頁面的建構 - Image, Text, Button

  • 分享至 

  • xImage
  •  

今天的主題會圍繞在三個常用的元件Image(圖片), Text(文字), Button(按鈕)
並用它們來實作

好的,那我們就開始吧!


Image

  • image元件用於顯示圖像
    • 依圖片來源分為不同的constructor: Image.asset, Image.network, Image.file...
    • 其他常用的圖片調整選項有:fit, scale, loadingBuilder , frameBuilder

Image.asset

  • 引用放到專案中的圖檔

    1. 在專案根目錄創建assets資料架
    2. assets/放入圖片,例如 assets/test.png
    3. pubspec.yaml中的flutter: 區塊下方指定圖片位置
      flutter:
        uses-material-design: true
      
        # 新增圖片,將asset圖片加入專案以便引用
        assets:
          - assets/test.png
      
      
    4. 在專案中引用圖片
      Image(image: AssetImage('assets/test.png'));
      // or
      Image.asset('assets/test.png')
      
      AssetImage是一種ImageProvider,用於提供特定路徑的圖像資源,Image.asset constructor內部就是使用AssetImage取得圖片的。
  • 由於這個方法是將圖檔加入專案中一起打包成應用程式,會造成App佔用空間變大,需仔細斟酌。

Image.network

  • 引用網路上的圖檔
    Image.network('https://WEB_SOURCE')
    

Image.file

  • 引用裝置上的圖檔
    Image.file(File('/storage/FILE_SOURCE'),)
    
    引用時需填入該檔案在裝置上的位置

圖像調整

  • fit: 調整圖像大小的策略, 通常使用BoxFit

    • BoxFit.contain:圖片小於外框時,在不變形的情況下盡可能地填滿外框
    • BoxFit.cover:圖片大於外框時,在不變形的情況下盡可能塞進外框,並將多餘的部分裁切
    • BoxFit.fill : 以圖片塞滿外框,可能造成變形
    • BoxFit.fitHeight:讓圖片垂直方向完全展示,不管水平方向是否有被裁切
    • BoxFit.fitWidth:讓圖片水平方向完全展示,不管垂直方向是否有被裁切
    • BoxFit.none:維持原圖
    • BoxFit.scaleDown:將圖片對準框中心並縮小圖片直到全圖皆可以完整顯示
  • scale: 對圖片進行縮放。輸入的數值代表在圖片長寬不變的情況下,增加垂直和水平的像素的倍數。例如scale:2.0代表此圖片範圍內,水平和垂直的像素數量皆變為兩倍,因此圖片本身看起來會縮小

  • loadingBuilder:設定在圖片在載入時所要顯示的元件

    如官網案例,顯示CircularProgressIndicator(載入中的轉圈圈元件),並指定這個轉圈圈的進度會比照載入圖片的百分比來進行:

    Image.network(
        'https://flutter.github.io/assets-for-api-docs/assets/widgets/falcon.jpg',
        fit: BoxFit.cover, 
        loadingBuilder:(
            BuildContext context, 
            Widget child,
            ImageChunkEvent? loadingProgress) {
              if (loadingProgress == null) {
                return child;
              }
              return Center(
                child: CircularProgressIndicator(
                  value: loadingProgress.expectedTotalBytes != null
                      ? loadingProgress.cumulativeBytesLoaded /
                          loadingProgress.expectedTotalBytes!
                      : null,
                ),
              );
            }
    )
    
  • frameBuilder: 設定圖片載入時的動畫效果

    如官方案例,建立過場動畫為透明淡出

    Image.network(
        'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
        frameBuilder: (BuildContext context, Widget child, int? frame,
            bool wasSynchronouslyLoaded) {
          if (wasSynchronouslyLoaded) {
            return child;
          }
          return AnimatedOpacity(
            opacity: frame == null ? 0 : 1,
            duration: const Duration(seconds: 1),
            curve: Curves.easeOut,
            child: child,
          );
        },
      ),
    
  • 更多Image的介紹,詳見Image class


Text

  • 用於顯示文字的組件
    • 第一個參數即為要顯示的文字
    • 透過style 參數設置文字的樣式
    Text(
      'Hello, $_name! How are you?',
      textAlign: TextAlign.center, // 將文字對齊置中
      overflow: TextOverflow.ellipsis, // 文字超出外框時會省略超出的部分改為'...'
      style: const TextStyle(fontWeight: FontWeight.bold),
    )
    

RichText

  • 當一組文字中需要有多種複雜的樣式,就可以使用RichText來呈現

    如下例,'Hello bold World!'中的'bold'要特別用粗體顯示,那我可以把這個文字組寫在RichText中,另外定義'bold'的樣式

    RichText(
      text: TextSpan(
        text: 'Hello ',
        style: DefaultTextStyle.of(context).style,
        children: const <TextSpan>[
          TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)),
          TextSpan(text: ' world!'),
        ],
      ),
    )
    
  • 更多關於Text的介紹,詳見Text Widgets


Button

  • 按鈕是一種點擊之後會觸發特定行為的組件,依照樣式分為TextButton, ElevatedButton和OutlinedButton

  • 透過style設定按鈕樣式,onPress設定點擊後要執行的函式,並透過child來決定它的子元件(顯示在按鈕內部的組件)。

TextButton

  • 一般顯示文字並可以點擊的按鈕

ElevatedButton

  • 背後有陰影看起來更為立體的按鈕

OutlinedButton

  • 以框線構成無填滿的按鈕

三者比較:

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    TextButton(
      style: TextButton.styleFrom(
        textStyle: const TextStyle(fontSize: 20),
      ),
      onPressed: () {},
      child: const Text('TextButton'),
    ),
    ElevatedButton(
      style: ElevatedButton.styleFrom(
          elevation: 9.0, textStyle: const TextStyle(fontSize: 20)),
      onPressed: () {},
      child: const Text('ElevatedButton'),
    ),
    OutlinedButton(
      style: OutlinedButton.styleFrom(
          textStyle: const TextStyle(fontSize: 20)),
      onPressed: () {},
      child: const Text('OutlinedButton'),
    )
  ],
),


專案實作

  • 我們直接用今天的APOD頁面當作例子,先大致看一下頁面呈現。未來會將所有寫死的靜態網址和文字都換成動態的資料。
  1. 加入圖片區塊,取得線上的圖片位址並使用Image.network()

    Image.network('https://apod.nasa.gov/apod/image/2209/WaterlessEarth2_woodshole_2520.jpg')
    
  2. 加入圖片載入時顯示的畫面:指示載入進度的圓圈

    Image.network(
        'https://apod.nasa.gov/apod/image/2209/WaterlessEarth2_woodshole_2520.jpg',
        loadingBuilder:(
            BuildContext context, 
            Widget child,
            ImageChunkEvent? loadingProgress) {
              if (loadingProgress == null) {
                return child;
              }
              return Center(
                child: CircularProgressIndicator(
                  value: loadingProgress.expectedTotalBytes != null
                      ? loadingProgress.cumulativeBytesLoaded /
                          loadingProgress.expectedTotalBytes!
                      : null,
                ),
              );
            }
        )
    )
    
  3. 使用ElevatedButton加入「add to favorite」按鈕

    ElevatedButton(
      onPressed: () {
        print('add to favorite');
      },
      child: const Text('favorite')),
    
  4. 加入文字說明區塊:複製今日APOD的說明並以Text呈現

    const Text(
        "How much of planet Earth is made of water? Very little, actually. Although oceans of water cover about 70 percent of Earth's surface, these oceans are shallow compared to the Earth's radius. The featured illustration shows what would happen if all of the water on or near the surface of the Earth were bunched up into a ball. The radius of this ball would be only about 700 kilometers, less than half the radius of the Earth's Moon, but slightly larger than Saturn's moon Rhea which, like many moons in our outer Solar System, is mostly water ice. The next smallest ball depicts all of Earth's liquid fresh water, while the tiniest ball shows the volume of all of Earth's fresh-water lakes and rivers. How any of this water came to be on the Earth and whether any significant amount is trapped far beneath Earth's surface remain topics of research.",
        style: TextStyle(fontSize: 12, color: Colors.blueGrey),
    )
    
  • 頁面呈現

    我們可以看到頁面上如實呈現了Image, ElevatedButtonText。但是畫面上有一個施工區塊,這是由於我們的畫面排版不當、有組件在版面上被切到,flutter於debug版本給我們的提示。
    不過別著急,我們在接下來的章節會修復這個問題~
  • 本次改動的相關程式碼放在我的github,見Day11相關commit

Recap

  • 今天介紹了
    • Image:圖片組件
      • Image.asset:載入專案內附帶的圖片
      • Image.network:載入網路上的圖片
      • Image.file:載入本地裝置的圖片
      • fit:圖片塑形的方式
      • scale:圖片縮放
      • loadingBuilder:載入圖片時的畫面
      • frameBuilder:載入圖片時的動畫
    • Text
      • Text:文字組件
      • RichText:多重樣式的文字組合組件
    • Button
      • TextButton:文字按鈕
      • ElevatedButton:立體按鈕
      • OutlinedButton:框線按鈕
  • 明天一起來看看Flutter的頁面佈局(Layout)原理吧!

上一篇
Flutter介紹:組件狀態管理
下一篇
Flutter介紹:頁面的排版 - layout
系列文
Flutter 30: from start to store30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言