Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

劉望舒發表於2019-06-05

本文首發於公眾號「劉望舒」

關聯絡列
ReactNative入門系列
React Native元件
Flutter基礎系列

前言

學完了Dart語言,接下來就可以學習Widget了,Flutter的UI介面就是由Widget組成的,Widget的數量繁多,因此我會用幾篇文章來專門介紹它,本篇就來介紹Basics Widget。

1.什麼是Widget

Flutter的Widget的設計靈感來自於React,主要目的就是使用Widget構建UI。Widget根據其當前配置和狀態來描述檢視,當Widget的狀態發生更改時,Widget會重建其描述。framework將根據前面的描述進行對比,以確定底層渲染樹從一個狀態轉換到下一個狀態所需的最小更改。 在Flutter中,除了Basics 的文字、圖片、卡片、輸入框這些基礎控制元件,佈局方式和動畫等也都是由Widget組成的。通過使用不同型別的Widget,就可以實現複雜的介面。 Widget可以翻譯為部件,粗略的相當於Android中的View。Widget和View不同的是:Widget具有不同的生命週期:它是不可變的,每當Widget或者其狀態發生變化時,Flutter的框架都會建立一個新的Widget例項樹。相比之下,Android中的View會被繪製一次,並且在invalidate呼叫之前不會重繪。

2.Widget的分類

Widget的分類有很多類別,每個類別下面又包含很多Widget,主要包括以下幾種類別:

  • Basics:在構建第一個Flutter應用程式之前,需要知道的Basics Widget。
  • Material Components:Material Design風格的Widget。
  • Cupertino:iOS風格的Widget。
  • Accessibility:輔助功能Widget。
  • Animation and Motion:動畫和動作Widget。
  • Async:Flutter應用程式的非同步Widget。
  • Input:除了在Material Components和Cupertino中的輸入Widget外,還可以接受使用者輸入的Widget。
  • Interaction Models:響應觸控事件並將使用者路由到不同的檢視中。
  • Layout:用於佈局的Widget。
  • Painting and effects:不改變佈局、大小、位置的情況下為子Widget應用視覺效果。
  • Scrolling:滾動相關的Widget。
  • Styling:主題、填充相關Widget。
  • Text:顯示文字和文字樣式。

Basics有些特殊,它是由Flutter官方從其他的Widget分類中選取的一些Widget組成的,這些Widget是官方建議開發者構建第一個Flutter應用程式之前,需要知道的,目的是讓開發者更快的入門。比如Row屬於Layout分類,它就被選進了Basics中。本文遵循了Flutter官方的意圖,首先介紹Basics(Basics Widget)。

Widget更多的是以組合的形式存在,比如Container是屬於Layout中的一個Widget,而Container又由LimitedBox、 ConstrainedBox、Align、 Padding、 DecoratedBox、Transform部件組成。 如果要實現Container的自定義效果,可以組合上面這些Widget以及其他簡單的Widget,而不是將Container進行子類化實現。

3.Widget的狀態分類

在Android中,我們可以通過直接更改View來更新檢視。但是在Flutter中,Widget是不可變的並且不會直接更新,而是必須使用Widget的狀態。 Widget有兩種狀態分類分別是無狀態的StatelessWidget和有狀態的StatefulWidget,StatelessWidget是不可變的,設定以後就不可再變化,所有的值都是最終的設定。StatefulWidget可以儲存自己的狀態,但是Widget是不可變的,因此需要配合State來儲存狀態。 State擁有自己的宣告週期,如下所示:

名稱 狀態
initState create之後被insert到渲染樹時呼叫的,只會呼叫一次
didChangeDependencies state依賴的物件發生變化時呼叫
didUpdateWidget Widget狀態改變時候呼叫,可能會呼叫多次
build 構建Widget時呼叫
deactivate 當移除渲染樹的時呼叫
dispose Widget即將銷燬時呼叫

4.根Widget的種類

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
     ...
        ),
      ),
    );
  }
}
複製程式碼

上面的MaterialApp就是一個根Widget,也就是Flutter應用程式的第一個Widget,根Widget有以下幾種:

  • WidgetsApp: 如果需要自定義風格,可以使用WidgetsApp。
  • MaterialApp:Material Design風格的Widget。
  • CupertinoApp iOS風格的根Widget。

如果公司沒有特殊要求,這裡建議使用MaterialApp做為根Widget就可以了。

5.Basics Widget

Basics Widget也就是Basics,主要有以下幾種:

  • Container:一個便利的容器Widget,可以設定Widget的背景、尺寸、定位。
  • Row:在水平方向上佈置子視窗Widget列表。
  • Column:在垂直方向上佈置子視窗Widge列表。
  • Image:顯示影象的Widget
  • Text:單一樣式的文字。
  • Icon:符合Material Design設計規範的圖示
  • RaisedButton:符合Material Design設計規範的凸起按鈕。
  • Scaffold:實現Basics 的Material Design佈局結構。
  • Appbar:Material Design的應用欄。
  • FlutterLogo:以Widget形式來展示一個Flutter圖示,可以調整樣式。
  • Placeholder:繪製一個框,為將來新增的Widget的佔位。

這裡選擇一些我們必須要掌握的Basics Widget來進行講解。

5.1 程式碼模板和主題

為了更好的理解這些Basics Widget,我們需要寫一些例子,這些例子需要一個程式碼模板,方便測試和學習。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(//1
      title: 'Welcome to Flutter',
      home: Scaffold(//2
        appBar: AppBar(//3
          title: Text('Basics Widget'),
        ),
        body:
          Padding(
            padding: EdgeInsets.all(40.0),
            child:  Text('在這裡編寫和測試其他Basics Widget'),
          ),
      ),
    );
  }
}
複製程式碼

上面的程式碼是稍微改動了官方的Hello World程式碼,便於測試,具體的程式碼含義已經在Flutter基礎(二)Flutter開發環境搭建和Hello World中講過了,這裡結合本文要講的內容再說點細節。註釋1處的MaterialApp屬於Material Components類別中的Widget,MaterialApp中包含了實現Material Design的應用程式所需要的Widget。 註釋2和3處的Scaffold和AppBar同樣也是Material Components類別中的Widget,Scaffold實現了Material Design佈局結構,AppBar是Material Design的應用欄,它們會在下一篇文章介紹Material Components時進行講解。效果如下圖所示:

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

5.2 文字

在4.1小節中已經用了Text,還可以定義樣式:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Basics Widget'),
        ),
        body: Padding(
          padding: EdgeInsets.all(60.0),
          child: Text(
            '文字樣式',
            style: TextStyle(
              fontSize: 16.0, 
              color: Colors.indigo,
              fontStyle: FontStyle.normal,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}
複製程式碼

這次為了便於理解列出了全部的程式碼,此後的舉例只列出改變的部分。 通過TextStyle來定義文字的樣式,效果如下:

VZrQ3V.png

5.3 圖片

Image的建構函式有多種:

  • new Image:從ImageProvider獲取圖片
  • new Image.asset:使用key從AssetBundle獲取圖片
  • new Image.network:載入網路圖片
  • new Image.file:從檔案中獲取圖片
  • new Image.memory:用於從Uint8List獲取圖片

Image的屬性有很多種,主要的屬性為fit,用於表示圖片的填充模式,引數型別為BoxFit,BoxFit的取值主要有以下幾種,示例圖片來自flutter官方。

contain 全圖顯示,保持原比例。

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
cover 全圖充滿,可能拉伸也可能被裁剪
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
fill 全圖顯示,通過拉伸來充滿目標框
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
fitHeight 圖片高度充滿目標框,可能拉伸也可能被裁剪
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
fitWidth 圖片寬度充滿目標框,可能拉伸也可能被裁剪
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
none 保持圖片的原始大小,剪裁掉位於目標框外的圖片部分
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget
scaleDown 與contain縮小影象的方式相同,只不過會在必要時縮小以確保圖片完全在目標框內,如果不縮小等同於none。
Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

child: Image.network(
            "https://upload-images.jianshu.io/upload_images/1417629-53f7d0902457cbe6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240",
            width: 260,
            height: 100,
            fit: BoxFit.fill,
          ),
複製程式碼

效果如下圖所示:

VZr8uF.png

5.4 凸起按鈕

凸起按鈕RaisedButton是符合Material Design設計規範的按鈕,可以通過onPressed來回撥按鈕的點選。

RaisedButton(
            onPressed: () => print("onPressed"),
            color: Colors.lightBlueAccent,
            child: Text('RaisedButton', style: TextStyle(fontSize: 10)),
          ),
複製程式碼

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

除了使用RaisedButton,flutter還提供了其他的按鈕,比如FlatButton、IconButton、FloatingActionButton等等,它們的使用方法和RaisedButton大同小異,這裡就不再贅述。

5.5 其他Widget

Basics Widget中的還有Row、Column、Container等Widget,這裡簡單介紹下。

Row Row用於在水平方向顯示陣列中的子元素Widget。

    child: Row(
            children: <Widget>[
              Icon(Icons.access_alarm),
              Icon(Icons.add_a_photo),
              Icon(Icons.add_call),
            ],
          ),
複製程式碼

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

垂直方向顯示陣列中的子元素Widget用Column,使用方法和Row一樣。這裡需要提到的是Expanded,可以用Expanded來配合Row和Column使用,用來填充剩餘的空間。

        Row(
            children: <Widget>[
              Icon(Icons.access_alarm),
              Icon(Icons.add_a_photo),
              Icon(Icons.add_call),
              Expanded(
                child: FittedBox(
                  fit: BoxFit.contain,
                  child: const FlutterLogo(),
                ),
                flex: 2,
              ),
              Expanded(
                child: Text(
                  "佔剩餘部分的三分之一",
                ),
                flex: 1,
              ),
            ],
          ),
複製程式碼

其中的Expanded的作用是在自己的尺寸範圍內縮放並且調整child位置,使得child適合其尺寸。FlutterLogo是Basics Widget中的一種,用於展示Flutter圖示。使用flex可以調整兩個Expanded的佔比。

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

Container 一個便利的容器Widget,可以設定Widget的背景、尺寸、定位。描述起來有些抽象,可以理解它和Android中的ViewGroup差不多。

      Container(
              decoration:BoxDecoration(
                color: Colors.lightGreen
              ),
            child: Text('Container'),
            padding: EdgeInsets.all(36.0),
            margin: EdgeInsets.all(10.0),
          ),
複製程式碼

Container的padding和margin屬性和Android中的作用是類似的:

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

總結

本文主要介紹了什麼是Widget、Widget的分類、Basics Widget。因為Widget的數量繁多,官方將Widget進行了分類,並將需要先了解的Widget歸入到了Basics Widget中,後續文章會介紹其他的Widge分類。


分享大前端、Android、Java等技術,助力5萬程式設計師成長進階。

Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget

相關文章