本文首發於公眾號「劉望舒」
關聯絡列
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時進行講解。效果如下圖所示:

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來定義文字的樣式,效果如下:

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 全圖顯示,保持原比例。







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,
),
複製程式碼
效果如下圖所示:

5.4 凸起按鈕
凸起按鈕RaisedButton是符合Material Design設計規範的按鈕,可以通過onPressed來回撥按鈕的點選。
RaisedButton(
onPressed: () => print("onPressed"),
color: Colors.lightBlueAccent,
child: Text('RaisedButton', style: TextStyle(fontSize: 10)),
),
複製程式碼

除了使用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),
],
),
複製程式碼

垂直方向顯示陣列中的子元素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的佔比。

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中的作用是類似的:

總結
本文主要介紹了什麼是Widget、Widget的分類、Basics Widget。因為Widget的數量繁多,官方將Widget進行了分類,並將需要先了解的Widget歸入到了Basics Widget中,後續文章會介紹其他的Widge分類。
分享大前端、Android、Java等技術,助力5萬程式設計師成長進階。
