給 Web 開發者的 Flutter 指南
本頁適用於熟悉 HTML 與 CSS 語法、用於安排應用程式 UI 元件(components)的使用者。這裡將 HTML/CSS 程式碼片段對應到其 Flutter/Dart 程式碼等價寫法。
Flutter 是一個用於構建跨平台應用程式的框架,採用 Dart 程式語言。
若想了解 Dart 與 JavaScript 程式設計之間的差異,請參考 Learning Dart as a JavaScript Developer。
設計 Web 版面與 Flutter 版面的一個根本差異,在於學習「限制條件(constraints)」的運作方式,以及元件(Widgets)如何被調整大小與定位。
欲深入了解,請參閱 Understanding constraints。
本頁範例假設:
HTML 文件以
<!DOCTYPE html>開頭,且所有 HTML 元素的 CSS box model 均設定為border-box,以符合 Flutter 的模型。css{ box-sizing: border-box; }在 Flutter 中,'Lorem ipsum' 文字的預設樣式是由
bold24Roboto變數定義,如下所示,以保持語法簡潔:dartTextStyle bold24Roboto = const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, );
執行基本版面配置操作
#以下範例展示如何執行最常見的 UI 版面配置任務。
文字樣式與對齊
#字型樣式、大小以及其他 CSS 透過 font 和 color 屬性處理的文字屬性,在 Flutter 中是 TextStyle 作為 Text 元件(Widget)的子屬性所單獨設定的屬性。
對於 CSS 中用於文字對齊的 text-align 屬性,Flutter 的 Text 元件(Widget)則有對應的 textAlign 屬性。
在 HTML 和 Flutter 中,子元素或元件(Widgets)預設皆會從左上角對齊。
<div class="grey-box">
Lorem ipsum
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Georgia;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: const Text(
'Lorem ipsum',
style: TextStyle(
fontFamily: 'Georgia',
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
);設定背景顏色
#在 Flutter 中,你可以使用 color 屬性或 Container 的 decoration 屬性來設定背景顏色。 然而,這兩個屬性不能同時指定,因為這可能會導致裝飾(decoration)覆蓋背景顏色。 當背景僅為單一顏色時,建議優先使用 color 屬性。 若需其他效果,例如漸層或圖片,則請使用 decoration 屬性。
CSS 範例中所使用的色碼,皆為 Material 色彩調色盤的十六進位對應值。
<div class="grey-box">
Lorem ipsum
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
);final container = Container(
// grey box
width: 320,
height: 240,
decoration: BoxDecoration(
color: Colors.grey[300],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
);置中元件
#Center 元件(Widget)會同時將其子元件在水平與垂直方向上置中。
若要在 CSS 中達到類似的效果,父元素會使用 flex 或 table-cell 的顯示行為。本頁範例展示的是 flex 行為。
<div class="grey-box">
Lorem ipsum
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
);設定容器寬度
#若要指定 Container 元件(Widget)的寬度,可以使用其 width 屬性。這是一個固定寬度,與 CSS 的 max-width 屬性不同,max-width 會根據最大值自動調整容器寬度。若要在 Flutter 中模擬這種效果,可以使用 Container 的 constraints 屬性。建立一個新的 BoxConstraints 元件(Widget),並搭配 minWidth 或 maxWidth 使用。
對於巢狀的 Container,如果父層的寬度小於子層的寬度,子層 Container 會自動調整大小以符合父層寬度。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
width: 100%;
max-width: 240px;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
width: 240, // max-width is 240
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
);操作位置與大小
#以下範例展示如何對元件(Widget)的位置、大小與背景進行更複雜的操作。
設定絕對位置
#預設情況下,元件會相對於其父元件進行定位。
若要以 x-y 座標為元件指定絕對位置,請將其巢狀於 Positioned 元件中,並再將其巢狀於 Stack 元件內。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
position: relative;
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
position: absolute;
top: 24px;
left: 24px;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Stack(
children: [
Positioned(
// red box
left: 24,
top: 24,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
],
),
);旋轉元件 (Rotating components)
#若要旋轉一個元件(Widget),請將其巢狀於 Transform 元件中。
使用 Transform 元件的 alignment 與 origin 屬性,分別以相對與絕對方式指定變形的原點(支點)。
若要進行簡單的 2D 旋轉(即在 Z 軸上旋轉元件),請建立一個新的 Matrix4 單位矩陣物件,並使用其 rotateZ() 方法,以弧度(度數 × π / 180)指定旋轉因子。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
transform: rotate(15deg);
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Transform(
alignment: Alignment.center,
transform: Matrix4.identity()..rotateZ(15 * 3.1415927 / 180),
child: Container(
// red box
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
textAlign: TextAlign.center,
),
),
),
),
);元件縮放
#若要放大或縮小一個元件(Widget),請將其巢狀於 Transform 元件中。 使用 Transform 元件的 alignment 和 origin 屬性, 分別以相對或絕對方式指定轉換的原點(支點)。
若要在 x 軸上進行簡單的縮放操作, 請建立一個新的 Matrix4 單位矩陣物件, 並使用其 scale() 方法來指定縮放比例。
當你縮放父元件時, 其子元件也會相應地被縮放。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
transform: scale(1.5);
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Transform(
alignment: Alignment.center,
transform: Matrix4.identity()..scaleByDouble(1.5, 1.5, 1.5, 1.5),
child: Container(
// red box
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
textAlign: TextAlign.center,
),
),
),
),
);套用線性漸層
#要將線性漸層應用於元件(Widget)的背景,請將其巢狀於 Container 元件中。然後,使用 Container 元件的 decoration 屬性來建立一個 BoxDecoration 物件,並利用 BoxDecoration 的 gradient 屬性來轉換背景填色。
漸層的「角度」是根據 Alignment(x, y)值決定的:
- 如果起點與終點的 x 值相同,則漸層為垂直方向(0° | 180°)。
- 如果起點與終點的 y 值相同,則漸層為水平方向(90° | 270°)。
垂直漸層
#<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
padding: 16px;
color: #ffffff;
background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%);
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment(0.0, 0.6),
colors: <Color>[
Color(0xffef5350),
Color(0x00ef5350),
],
),
),
padding: const EdgeInsets.all(16),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
);水平漸層
#<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
padding: 16px;
color: #ffffff;
background: linear-gradient(90deg, #ef5350, rgba(0, 0, 0, 0) 80%);
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment(-1.0, 0.0),
end: Alignment(0.6, 0.0),
colors: <Color>[
Color(0xffef5350),
Color(0x00ef5350),
],
),
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
);操作圖形
#以下範例展示如何建立與自訂圖形。
圓角處理
#若要讓矩形圖形的四個角變成圓角,請使用 BoxDecoration 物件的 borderRadius 屬性。 建立一個新的 BorderRadius 物件,來指定每個角落圓角的半徑。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
border-radius: 8px;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red circle
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
borderRadius: const BorderRadius.all(
Radius.circular(8),
),
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
);新增 box shadows(盒子陰影)
#在 CSS 中,你可以使用 box-shadow 屬性,以簡寫方式指定陰影的偏移量和模糊程度。以下範例顯示了兩個 box shadow,其屬性為:
xOffset: 0px, yOffset: 2px, blur: 4px, color: black @80% alphaxOffset: 0px, yOffset: 06x, blur: 20px, color: black @50% alpha
在 Flutter 中,每個屬性和值都需要分別指定。請使用 BoxDecoration 的 boxShadow 屬性,建立一個 BoxShadow 元件(Widgets)清單。你可以定義一個或多個 BoxShadow 元件(Widgets),這些元件可以堆疊,以自訂陰影的深度、顏色等效果。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8),
0 6px 20px rgba(0, 0, 0, 0.5);
}final container = Container(
// grey box
width: 320,
height: 240,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.grey[300],
),
child: Center(
child: Container(
// red box
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
boxShadow: const <BoxShadow>[
BoxShadow(
color: Color(0xcc000000),
offset: Offset(0, 2),
blurRadius: 4,
),
BoxShadow(
color: Color(0x80000000),
offset: Offset(0, 6),
blurRadius: 20,
),
],
),
child: Text(
'Lorem ipsum',
style: bold24Roboto,
),
),
),
);製作圓形與橢圓形
#在 CSS 中製作圓形,通常需要透過將矩形的四個邊的 border-radius 設為 50% 來達成,雖然也有基本形狀可用。
這種做法可以透過 BoxDecoration 的 borderRadius 屬性來實現, 而在 Flutter 中,則提供了 shape 屬性,並搭配 BoxShape 列舉 來達成這個目的。
<div class="grey-box">
<div class="red-circle">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-circle {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
text-align: center;
width: 160px;
height: 160px;
border-radius: 50%;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red circle
decoration: BoxDecoration(
color: Colors.red[400],
shape: BoxShape.circle,
),
padding: const EdgeInsets.all(16),
width: 160,
height: 160,
child: Text(
'Lorem ipsum',
style: bold24Roboto,
textAlign: TextAlign.center,
),
),
),
);操作文字
#以下範例說明如何指定字型及其他文字屬性,也展示了如何轉換文字字串、自訂間距,以及建立摘要。
調整文字間距
#在 CSS 中,你可以透過設定 letter-spacing 和 word-spacing 屬性來指定每個字母或單字之間的空白量。這些屬性的值可以是 px、pt、cm、em 等單位。
在 Flutter 中,你可以針對 TextStyle 的 letterSpacing 和 wordSpacing 屬性(這個元件作為 Text 元件的子元件)來指定空白量,單位為邏輯像素(允許負值)。
<div class="grey-box">
<div class="red-box">
Lorem ipsum
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
letter-spacing: 4px;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red[400],
),
child: const Text(
'Lorem ipsum',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w900,
letterSpacing: 4,
),
),
),
),
);進行行內格式變更
#Text 元件(Widget)可讓你顯示具有部分格式化特性的文字。
若要顯示具有多種樣式的文字(例如本例中,單一單字加強顯示),請改用 RichText 元件(Widget)。
其 text 屬性可以指定一個或多個 TextSpan 物件,每個物件都可個別設定樣式。
在以下範例中,"Lorem" 使用預設(繼承)文字樣式放在 TextSpan 中,
而 "ipsum" 則放在另一個 TextSpan 中,並套用自訂樣式。
<div class="grey-box">
<div class="red-box">
Lorem <em>ipsum</em>
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
}
.red-box em {
font: 300 48px Roboto;
font-style: italic;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
decoration: BoxDecoration(
color: Colors.red[400],
),
padding: const EdgeInsets.all(16),
child: RichText(
text: TextSpan(
style: bold24Roboto,
children: const <TextSpan>[
TextSpan(text: 'Lorem '),
TextSpan(
text: 'ipsum',
style: TextStyle(
fontWeight: FontWeight.w300,
fontStyle: FontStyle.italic,
fontSize: 48,
),
),
],
),
),
),
),
);建立文字摘要
#摘要會顯示段落的起始行(或多行)文字,並處理溢出的文字,通常會使用省略號來表示。
在 Flutter 中,可以使用 Text 元件(Widget)的 maxLines 屬性來指定摘要中要顯示的行數,並透過 overflow 屬性來處理溢出的文字。
<div class="grey-box">
<div class="red-box">
Lorem ipsum dolor sit amet, consec etur
</div>
</div>
.grey-box {
background-color: #e0e0e0; /* grey 300 */
width: 320px;
height: 240px;
font: 900 24px Roboto;
display: flex;
align-items: center;
justify-content: center;
}
.red-box {
background-color: #ef5350; /* red 400 */
padding: 16px;
color: #ffffff;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}final container = Container(
// grey box
width: 320,
height: 240,
color: Colors.grey[300],
child: Center(
child: Container(
// red box
decoration: BoxDecoration(
color: Colors.red[400],
),
padding: const EdgeInsets.all(16),
child: Text(
'Lorem ipsum dolor sit amet, consec etur',
style: bold24Roboto,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
);