給 Web 開發者的 Flutter 指南
本文是為那些熟悉用 HTML 與 CSS 語法來管理應用頁面中元素的開發者準備的。本文會將 HTML/CSS 程式碼片段替換為等價的 Flutter/Dart 程式碼。
Flutter 是一個用於建構跨平臺應用的框架,它使用 Dart 程式語言。要了解 Dart 程式語言與 Javascript 程式語言的異同,請參考文件 給 JavaSript 開發者的 Dart 程式語言指南。
在 Web 和 Flutter 的佈局基礎條件中, 佈局限制、widget 的大小確定和定位 是重要的區別之一。想要了解更多,你可以閱讀 深入理解 Flutter 佈局約束。
以下的範例基於如下假設:
-
HTML 檔案以
<!DOCTYPE html>
開頭,且為了與 Flutter 模型保持一致,所有 HTML 元素的 CSS 盒模型被設定為border-box
。{ box-sizing: border-box; }
-
在 Flutter 中,為了保持語法簡潔, “Lorem ipsum” 文字的預設樣式由如下
bold24Roboto
變數定義:TextStyle bold24Roboto = const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, );
執行基礎佈局操作
以下範例將向你展示如何執行最常見的 UI 佈局操作。
文字樣式與對齊
CSS 所處理的字型樣式、大小以及其他文字屬性,都是一個 Text
widget 子元素 TextStyle
中單獨的屬性。
Text
widget 中的 textAlign 屬性與 CSS 中的
text-align 屬性作用相同,用來控制文字的對齊方向。
在 HTML 和 Flutter 中,子元素或者 widget 的位置都預設在左上方。
<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 中,你可以透過 Container
的
decoration
或者 color
屬性來設定背景顏色。但是,你不能同時設定這兩個屬性,這有可能導致 decoration
覆蓋掉 color
。當背景是簡單的顏色時,應首選 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 可以將它的子 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 屬性用於指定容器可調整的寬度最大值不同的是,這裡指定的是一個固定寬度。要在 Flutter 中模擬該效果,可以使用 Container 的 constraints
屬性。新建一個帶有 minWidth
和 maxWidth
屬性的 BoxConstraints
widget。
對巢狀(Nesting)的 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 的位置、大小以及背景進行更復雜的操作。
設定絕對位置
預設情況下,widget 相對於其父元素定位。
想要透過 x-y 座標指定一個 widget 的絕對位置,可以把它放在一個 Positioned
widget 中,而 Positioned
則需被放在一個 Stack
widget 中。
<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,
),
),
),
],
),
);
旋轉元素
想要旋轉一個 widget,請將它放在 Transform
widget 中。使用 Transform
widget 的 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
widget 中。使用 Transform
widget 的 alignment
和 origin
屬性分別來指定縮放原點(支點)的相對和絕對資訊。
對於沿 x 軸的簡單縮放操作,新建一個 Matrix4
物件並用它的 scale()
方法來指定縮放因係數。
當你縮放一個父 widget 時,它的子 widget 也會相應被縮放。
<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()..scale(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 的背景上應用,請將它巢狀(Nesting)在一個 Container
widget 中。接著將一個 BoxDecoration
物件傳遞至 Container
的 decoration
,然後使用 BoxDecoration
的 gradient
屬性來變換背景填充內容。
變換「角度」基於 Alignment (x, y)
取值來定:
-
If the beginning and ending x values are equal, the gradient is vertical (0° | 180°).
如果開始和結束的 x 值相同,變換將是垂直的 (0° 180°)。 -
If the beginning and ending y values are equal, the gradient is horizontal (90° | 270°).
如果開始和結束的 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 屬性快速指定陰影偏移與模糊範圍。本例展示了兩個盒陰影的屬性設定:
xOffset: 0px, yOffset: 2px, blur: 4px, color: black @80% alpha
xOffset: 0px, yOffset: 06x, blur: 20px, color: black @50% alpha
在 Flutter 中,每個屬性與其取值都是單獨指定的。請使用 BoxDecoration 的 boxShadow
屬性來產生一系列 BoxShadow
widget。你可以定義一個或多個 BoxShadow
widget,這些 widget 共同用於設定陰影深度、顏色等等。
<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 中有 基礎圖形,用 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 中,你可以將 Text
widget 的 TextStyle
屬性中
letterSpacing
與 wordSpacing
設定為邏輯畫素(允許負值)。
<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,
),
),
),
);