1. 前言
Flutter
作為時(shí)下最流行的技術(shù)之一良姆,憑借其出色的性能以及抹平多端的差異優(yōu)勢(shì)爵赵,早已引起大批技術(shù)愛(ài)好者的關(guān)注垮媒,甚至一些閑魚(yú)
吮成,美團(tuán)
玻驻,騰訊
等大公司均已投入生產(chǎn)使用竹捉。雖然目前其生態(tài)還沒(méi)有完全成熟违帆,但身靠背后的Google
加持挣磨,其發(fā)展速度已經(jīng)足夠驚人豪直,可以預(yù)見(jiàn)將來(lái)對(duì)Flutter
開(kāi)發(fā)人員的需求也會(huì)隨之增長(zhǎng)劣摇。
無(wú)論是為了現(xiàn)在的技術(shù)嘗鮮還是將來(lái)的潮流趨勢(shì),都9102年了弓乙,作為一個(gè)前端開(kāi)發(fā)者末融,似乎沒(méi)有理由不去嘗試它。正是帶著這樣的心理暇韧,筆者也開(kāi)始學(xué)習(xí)Flutter
勾习,同時(shí)建了一個(gè)用于練習(xí)的倉(cāng)庫(kù),后續(xù)所有代碼都會(huì)托管在上面懈玻,歡迎star巧婶,一起學(xué)習(xí)。這是我寫的Flutter系列文章:
- 用Flutter構(gòu)建漂亮的UI界面 - 基礎(chǔ)組件篇
- Flutter滾動(dòng)型容器組件 - ListView篇
- Flutter網(wǎng)格型布局 - GridView篇
- 在Flutter中使用自定義Icon
今天分享的是Flutter中最常用到的一些基礎(chǔ)組件涂乌,它們是構(gòu)成UI界面的基礎(chǔ)元素:容器
艺栈,行
,列
湾盒,絕對(duì)定位布局
湿右,文本
,圖片
和圖標(biāo)
等罚勾。
2. 基礎(chǔ)組件
2.1 Container(容器組件)
Container
組件是最常用的布局組件之一毅人,可以認(rèn)為它是web開(kāi)發(fā)中的div
吭狡,rn開(kāi)發(fā)中的View
。其往往可以用來(lái)控制大小堰塌、背景顏色赵刑、邊框、陰影场刑、內(nèi)外邊距和內(nèi)容排列方式等般此。我們先來(lái)看下其構(gòu)造函數(shù):
Container({
Key key,
double width,
double height,
this.margin,
this.padding,
Color color,
this.alignment,
BoxConstraints constraints,
Decoration decoration,
this.foregroundDecoration,
this.transform,
this.child,
})
2.1.1 width
,height
牵现,margin
铐懊,padding
這些屬性的含義和我們已經(jīng)熟知的并沒(méi)有區(qū)別。唯一需要注意的是瞎疼,margin
和padding
的賦值不是一個(gè)簡(jiǎn)單的數(shù)字科乎,因?yàn)槠溆?code>left, top
, right
, bottom
四個(gè)方向的值需要設(shè)置。Flutter
提供了EdgeInsets
這個(gè)類贼急,幫助我們方便地生成四個(gè)方向的值茅茂。通常情況下,我們可能會(huì)用到EdgeInsets
的4種構(gòu)造方法:
-
EdgeInsets.all(value)
: 用于設(shè)置4個(gè)方向一樣的值太抓; -
EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4)
: 可以單獨(dú)設(shè)置某個(gè)方向的值空闲; -
EdgeInsets.symmetric(horizontal: val1, vertical: val2)
: 用于設(shè)置水平/垂直方向上的值; -
EdgeInsets.fromLTRB(left, top, right, bottom)
: 按照左上右下的順序設(shè)置4個(gè)方向的值走敌。
2.1.2 color
該屬性的含義是背景顏色碴倾,等同于web/rn中的backgroundColor。需要注意的是Flutter
中有一個(gè)專門表示顏色的Color
類掉丽,而非我們常用的字符串跌榔。不過(guò)我們可以非常輕松地進(jìn)行轉(zhuǎn)換,舉個(gè)栗子:
在web/rn中我們會(huì)用'#FF0000'
或'red'
來(lái)表示紅色捶障,而在Flutter中僧须,我們可以用Color(0xFFFF0000)
或Colors.red
來(lái)表示。
2.1.3 alignment
該屬性是用來(lái)決定Container
組件的子組件將以何種方式進(jìn)行排列(PS:再也不用為怎么居中操心了)残邀。其可選值通常會(huì)用到:
-
Alignment.topLeft
: 左上 -
Alignment.topCenter
: 上中 -
Alignment.topRight
: 右上 -
Alignment.centerLeft
: 左中 -
Alignment.center
: 居中 -
Alignment.centerRight
: 右中 -
Alignment.bottomLeft
: 左下 -
Alignment.bottomCenter
: 下中 -
Alignment.bottomRight
: 右下
2.1.4 constraints
在web/rn中我們通常會(huì)用minWidth
/maxWidth
/minHeight
/maxHeight
等屬性來(lái)限制容器的寬高皆辽。在Flutter
中,你需要使用BoxConstraints
(盒約束)來(lái)實(shí)現(xiàn)該功能芥挣。
// 容器的大小將被限制在[100*100 ~ 200*200]內(nèi)
BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 100,
maxHeight: 200,
)
2.1.5 decoration
該屬性非常強(qiáng)大驱闷,字面意思是裝飾,因?yàn)橥ㄟ^(guò)它你可以設(shè)置邊框
空免,陰影
空另,漸變
,圓角
等常用屬性蹋砚。BoxDecoration
繼承自Decoration
類扼菠,因此我們通常會(huì)生成一個(gè)BoxDecoration
實(shí)例來(lái)設(shè)置這些屬性摄杂。
1) 邊框
可以用Border.all
構(gòu)造函數(shù)直接生成4條邊框,也可以用Border
構(gòu)造函數(shù)單獨(dú)設(shè)置不同方向上的邊框循榆。不過(guò)令人驚訝的是官方提供的邊框竟然不支持虛線
(issue在這里)析恢。
// 同時(shí)設(shè)置4條邊框:1px粗細(xì)的黑色實(shí)線邊框
BoxDecoration(
border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
)
// 設(shè)置單邊框:上邊框?yàn)?px粗細(xì)的黑色實(shí)線邊框,右邊框?yàn)?px粗細(xì)的紅色實(shí)線邊框
BoxDecoration(
border: Border(
top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
),
)
2) 陰影
陰影屬性和web中的boxShadow
幾乎沒(méi)有區(qū)別秧饮,可以指定x
映挂,y
,blur
盗尸,spread
柑船,color
等屬性。
BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(0, 0),
blurRadius: 6,
spreadRadius: 10,
color: Color.fromARGB(20, 0, 0, 0),
),
],
)
3) 漸變
如果你不想容器的背景顏色是單調(diào)的泼各,可以嘗試用gradient
屬性鞍时。Flutter
同時(shí)支持線性漸變
和徑向漸變
:
// 從左到右,紅色到藍(lán)色的線性漸變
BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.red, Colors.blue],
),
)
// 從中心向四周擴(kuò)散扣蜻,紅色到藍(lán)色的徑向漸變
BoxDecoration(
gradient: RadialGradient(
center: Alignment.center,
colors: [Colors.red, Colors.blue],
),
)
4) 圓角
通常情況下逆巍,你可能會(huì)用到BorderRadius.circular
構(gòu)造函數(shù)來(lái)同時(shí)設(shè)置4個(gè)角的圓角,或是BorderRadius.only
構(gòu)造函數(shù)來(lái)單獨(dú)設(shè)置某幾個(gè)角的圓角:
// 同時(shí)設(shè)置4個(gè)角的圓角為5
BoxDecoration(
borderRadius: BorderRadius.circular(5),
)
// 設(shè)置單圓角:左上角的圓角為5莽使,右上角的圓角為10
BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(10),
),
)
2.1.6 transform
transform
屬性和我們?cè)趙eb/rn中經(jīng)常用到的基本也沒(méi)有差別蒸苇,主要包括:平移
,縮放
吮旅、旋轉(zhuǎn)
和傾斜
。在Flutter中味咳,封裝了矩陣變換類Matrix4
幫助我們進(jìn)行變換:
-
translationValues(x, y, z)
: 平移x, y, z庇勃; -
rotationX(radians)
: x軸旋轉(zhuǎn)radians弧度; -
rotationY(radians)
: y軸旋轉(zhuǎn)radians弧度槽驶; -
rotationZ(radians)
: z軸旋轉(zhuǎn)radians弧度责嚷; -
skew(alpha, beta)
: x軸傾斜alpha度,y軸傾斜beta度掂铐; -
skewX(alpha)
: x軸傾斜alpha度罕拂; -
skewY(beta)
: y軸傾斜beta度;
2.1.7 小結(jié)
Container
組件的屬性很豐富全陨,雖然有些用法上和web/rn有些許差異爆班,但基本上大同小異,所以過(guò)渡起來(lái)也不會(huì)有什么障礙辱姨。另外柿菩,由于Container
組件是單子節(jié)點(diǎn)組件,也就是只允許子節(jié)點(diǎn)有一個(gè)雨涛。所以在布局上枢舶,很多時(shí)候我們會(huì)用Row
和Column
組件進(jìn)行行
/列
布局懦胞。
2.2 Row/Column(行/列組件)
Row
和Column
組件其實(shí)和web/rn中的Flex布局
(彈性盒子)特別相似,或者我們可以就這么理解凉泄。使用Flex布局
的同學(xué)對(duì)主軸
和次軸
的概念肯定都已經(jīng)十分熟悉躏尉,Row
組件的主軸就是橫向,Column
組件的主軸就是縱向后众。且它們的構(gòu)造函數(shù)十分相似(已省略不常用屬性):
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
MainAxisSize mainAxisSize = MainAxisSize.max,
List<Widget> children = const <Widget>[],
})
2.2.1 mainAxisAlignment
該屬性的含義是主軸排列方式胀糜,根據(jù)上述構(gòu)造函數(shù)可以知道Row
和Column
組件在主軸方向上默認(rèn)都是從start開(kāi)始,也就是說(shuō)Row
組件默認(rèn)從左到右開(kāi)始排列子組件吼具,Column
組件默認(rèn)從上到下開(kāi)始排列子組件僚纷。
當(dāng)然,你還可以使用其他的可選值:
- MainAxisAlignment.start
- MainAxisAlignment.end
- MainAxisAlignment.center
- MainAxisAlignment.spaceBetween
- MainAxisAlignment.spaceAround
- MainAxisAlignment.spaceEvenly
2.2.2 crossAxisAlignment
該屬性的含義是次軸排列方式拗盒,根據(jù)上述構(gòu)造函數(shù)可以知道Row
和Column
組件在次軸方向上默認(rèn)都是居中怖竭。
這里有一點(diǎn)需要特別注意:由于Column
組件次軸方向上(即水平)默認(rèn)是居中對(duì)齊,所以水平方向上不會(huì)撐滿其父容器陡蝇,此時(shí)需要指定CrossAxisAlignment.stretch
才可以痊臭。
另外,crossAxisAlignment其他的可選值有:
- crossAxisAlignment.start
- crossAxisAlignment.end
- crossAxisAlignment.center
- crossAxisAlignment.stretch
- crossAxisAlignment.baseline
2.2.3 mainAxisSize
字面意思上來(lái)說(shuō)登夫,該屬性指的是在主軸上的尺寸广匙。其實(shí)就是指在主軸方向上,是包裹其內(nèi)容恼策,還是撐滿其父容器鸦致。它的可選值有MainAxisSize.min
和MainAxisSize.max
。由于其默認(rèn)值都是MainAxisSize.max
涣楷,所以主軸方向上默認(rèn)大小都是盡可能撐滿父容器的分唾。
2.2.4 小結(jié)
由于Row
/Column
組件和我們熟悉的Flex布局
非常相似,所以上手起來(lái)非常容易狮斗,幾乎零學(xué)習(xí)成本绽乔。
2.3 Stack/Positoned(絕對(duì)定位布局組件)
絕對(duì)定位布局在web/rn開(kāi)發(fā)中也是使用頻率較高的一種布局方式,Flutter
也提供了相應(yīng)的組件實(shí)現(xiàn)碳褒,需要將Stack
和Positioned
組件搭配在一起使用折砸。比如下方的這個(gè)例子就是創(chuàng)建了一個(gè)黃色的盒子,并且在其四個(gè)角落放置了4個(gè)紅色的小正方形沙峻。Stack
組件就是絕對(duì)定位的容器睦授,Positioned
組件通過(guò)left
,top
专酗,right
睹逃,bottom
四個(gè)方向上的屬性值來(lái)決定其在父容器中的位置。
Container(
height: 100,
color: Colors.yellow,
child: Stack(
children: <Widget>[
Positioned(
left: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
top: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
left: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
Positioned(
right: 10,
bottom: 10,
child: Container(width: 10, height: 10, color: Colors.red),
),
],
),
)
2.4 Text(文本組件)
Text
組件也是日常開(kāi)發(fā)中最常用的基礎(chǔ)組件之一,我們通常用它來(lái)展示文本信息沉填。來(lái)看下其構(gòu)造函數(shù)(已省略不常用屬性):
const Text(
this.data, {
Key key,
this.style,
this.textAlign,
this.softWrap,
this.overflow,
this.maxLines,
})
-
data
: 顯示的文本信息疗隶; -
style
: 文本樣式,Flutter
提供了一個(gè)TextStyle
類翼闹,最常用的fontSize
斑鼻,fontWeight
,color
猎荠,backgroundColor
和shadows
等屬性都是通過(guò)它設(shè)置的坚弱; -
textAlign
: 文字對(duì)齊方式,常用可選值有TextAlign
的left
关摇,right
荒叶,center
和justify
; -
softWrap
: 文字是否換行输虱; -
overflow
: 當(dāng)文字溢出的時(shí)候些楣,以何種方式處理(默認(rèn)直接截?cái)啵宪睹?蛇x值有TextOverflow
的clip
愁茁,fade
,ellipsis
和visible
亭病; -
maxLines
: 當(dāng)文字超過(guò)最大行數(shù)還沒(méi)顯示完的時(shí)候鹅很,就會(huì)根據(jù)overflow
屬性決定如何截?cái)嗵幚怼?/li>
Flutter
的Text
組件足夠靈活,提供了各種屬性讓我們定制罪帖,不過(guò)一般情況下促煮,我們更多地只需下方幾行代碼就足夠了:
Text(
'這是測(cè)試文本',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Color(0xFF999999),
),
)
除了上述的應(yīng)用場(chǎng)景外,有時(shí)我們還會(huì)遇到富文本
的需求(即一段文本中整袁,可能需要不同的字體樣式)污茵。比如在一些UI設(shè)計(jì)中經(jīng)常會(huì)遇到表示價(jià)格的時(shí)候,¥
符號(hào)比金額
的字號(hào)小點(diǎn)葬项。對(duì)于此類需求,我們可以用Flutter
提供的Text.rich
構(gòu)造函數(shù)來(lái)創(chuàng)建相應(yīng)的文本組件:
Text.rich(TextSpan(
children: [
TextSpan(
'¥',
style: TextStyle(
fontSize: 12,
color: Color(0xFFFF7528),
),
),
TextSpan(
'258',
style: TextStyle(
fontSize: 15,
color: Color(0xFFFF7528),
),
),
]
))
2.5 Image(圖片組件)
Image
圖片組件作為豐富內(nèi)容的基礎(chǔ)組件之一迹蛤,日常開(kāi)發(fā)中的使用頻率也非常高民珍。看下其構(gòu)造函數(shù)(已省略不常用屬性):
Image({
Key key,
@required this.image,
this.width,
this.height,
this.color,
this.fit,
this.repeat = ImageRepeat.noRepeat,
})
-
image
: 圖片源盗飒,最常用到主要有兩種(AssetImage
和NetworkImage
)嚷量。使用AssetImage
之前,需要在pubspec.yaml
文件中聲明好圖片資源逆趣,然后才能使用蝶溶;而NextworkImage
指定圖片的網(wǎng)絡(luò)地址即可,主要是在加載一些網(wǎng)絡(luò)圖片時(shí)會(huì)用到; -
width
: 圖片寬度抖所; -
height
: 圖片高度梨州; -
color
: 圖片的背景顏色,當(dāng)網(wǎng)絡(luò)圖片未加載完畢之前田轧,會(huì)顯示該背景顏色暴匠; -
fit
: 當(dāng)我們希望圖片根據(jù)容器大小進(jìn)行適配而不是指定固定的寬高值時(shí),可以通過(guò)該屬性來(lái)實(shí)現(xiàn)傻粘。其可選值有BoxFit
的fill
每窖,contain
,cover
弦悉,fitWidth
窒典,fitHeight
,none
和scaleDown
稽莉; -
repeat
: 決定當(dāng)圖片實(shí)際大小不足指定大小時(shí)是否使用重復(fù)效果瀑志。
另外,Flutter
還提供了Image.network
和Image.asset
構(gòu)造函數(shù)肩祥,其實(shí)是語(yǔ)法糖后室。比如下方的兩段代碼結(jié)果是完全一樣的:
Image(
image: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg'),
width: 100,
height: 100,
)
Image.network(
'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1402367109,4157195964&fm=27&gp=0.jpg',
width: 100,
height: 100,
)
2.6 Icon
(圖標(biāo)組件)
Icon
圖標(biāo)組件相比于圖片有著放大不會(huì)失真的優(yōu)勢(shì),在日常開(kāi)發(fā)中也是經(jīng)常會(huì)被用到混狠。Flutter
更是直接內(nèi)置了一套Material
風(fēng)格的圖標(biāo)(你可以在這里預(yù)覽所有的圖標(biāo)類型)岸霹。看下構(gòu)造函數(shù):
const Icon(
this.icon, {
Key key,
this.size,
this.color,
})
-
icon
: 圖標(biāo)類型将饺; -
size
: 圖標(biāo)大泄北堋; -
color
: 圖標(biāo)顏色予弧。
3. 布局實(shí)戰(zhàn)
通過(guò)上一節(jié)的介紹刮吧,我們對(duì)Container
,Row
掖蛤,Column
杀捻,Stack
,Positioned
蚓庭,Text
致讥,Image
和Icon
組件有了初步的認(rèn)識(shí)。接下來(lái)器赞,就讓我們通過(guò)一個(gè)實(shí)際的例子來(lái)加深理解和記憶垢袱。
3.1 準(zhǔn)備工作 - 數(shù)據(jù)類型
根據(jù)上述卡片中的內(nèi)容,我們可以定義一些字段港柜。為了規(guī)范開(kāi)發(fā)流程请契,我們先給卡片定義一個(gè)數(shù)據(jù)類型的類,這樣在后續(xù)的開(kāi)發(fā)過(guò)程中也能更好地對(duì)數(shù)據(jù)進(jìn)行Mock和管理:
class PetCardViewModel {
/// 封面地址
final String coverUrl;
/// 用戶頭像地址
final String userImgUrl;
/// 用戶名
final String userName;
/// 用戶描述
final String description;
/// 話題
final String topic;
/// 發(fā)布時(shí)間
final String publishTime;
/// 發(fā)布內(nèi)容
final String publishContent;
/// 回復(fù)數(shù)量
final int replies;
/// 喜歡數(shù)量
final int likes;
/// 分享數(shù)量
final int shares;
const PetCardViewModel({
this.coverUrl,
this.userImgUrl,
this.userName,
this.description,
this.topic,
this.publishTime,
this.publishContent,
this.replies,
this.likes,
this.shares,
});
}
3.2 搭建骨架,布局拆分
根據(jù)給的視覺(jué)圖爽锥,我們可以將整體進(jìn)行拆分涌韩,一共劃分成4個(gè)部分:Cover
,UserInfo
救恨,PublishContent
和InteractionArea
贸辈。為此,我們可以搭起代碼的基本骨架:
class PetCard extends StatelessWidget {
final PetCardViewModel data;
const PetCard({
Key key,
this.data,
}) : super(key: key);
Widget renderCover() {
}
Widget renderUserInfo() {
}
Widget renderPublishContent() {
}
Widget renderInteractionArea() {
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
blurRadius: 6,
spreadRadius: 4,
color: Color.fromARGB(20, 0, 0, 0),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
this.renderCover(),
this.renderUserInfo(),
this.renderPublishContent(),
this.renderInteractionArea(),
],
),
);
}
}
3.3 封面區(qū)域
為了更好的凸現(xiàn)圖片的效果肠槽,這里加了一個(gè)蒙層擎淤,所以此處剛好可以用得上Stack
/Positioned
布局和LinearGradient
漸變,Dom結(jié)構(gòu)如下:
Widget renderCover() {
return Stack(
fit: StackFit.passthrough,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
child: Image.network(
data.coverUrl,
height: 200,
fit: BoxFit.fitWidth,
),
),
Positioned(
left: 0,
top: 100,
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromARGB(0, 0, 0, 0),
Color.fromARGB(80, 0, 0, 0),
],
),
),
),
),
],
);
}
3.4 用戶信息區(qū)域
用戶信息區(qū)域就非常適合使用Row
和Column
組件來(lái)進(jìn)行布局秸仙,Dom結(jié)構(gòu)如下:
Widget renderUserInfo() {
return Container(
margin: EdgeInsets.only(top: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
CircleAvatar(
radius: 20,
backgroundColor: Color(0xFFCCCCCC),
backgroundImage: NetworkImage(data.userImgUrl),
),
Padding(padding: EdgeInsets.only(left: 8)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
data.userName,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
Padding(padding: EdgeInsets.only(top: 2)),
Text(
data.description,
style: TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
],
),
Text(
data.publishTime,
style: TextStyle(
fontSize: 13,
color: Color(0xFF999999),
),
),
],
),
);
}
3.5 發(fā)布內(nèi)容區(qū)域
通過(guò)這塊區(qū)域的UI練習(xí)嘴拢,我們可以實(shí)踐Container
組件設(shè)置不同的borderRadius
,以及Text
組件文本內(nèi)容超出時(shí)的截?cái)嗵幚砑偶停珼om結(jié)構(gòu)如下:
Widget renderPublishContent() {
return Container(
margin: EdgeInsets.only(top: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 14),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Color(0xFFFFC600),
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: Text(
'# ${data.topic}',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
),
),
Text(
data.publishContent,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
],
),
);
}
3.6 互動(dòng)區(qū)域
在這個(gè)模塊席吴,我們會(huì)用到Icon
圖標(biāo)組件,可以控制其大小和顏色等屬性捞蛋,Dom結(jié)構(gòu)如下:
Widget renderInteractionArea() {
return Container(
margin: EdgeInsets.symmetric(vertical: 16),
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Icon(
Icons.message,
size: 16,
color: Color(0xFF999999),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.replies.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
Row(
children: <Widget>[
Icon(
Icons.favorite,
size: 16,
color: Color(0xFFFFC600),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.likes.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
Row(
children: <Widget>[
Icon(
Icons.share,
size: 16,
color: Color(0xFF999999),
),
Padding(padding: EdgeInsets.only(left: 6)),
Text(
data.shares.toString(),
style: TextStyle(
fontSize: 15,
color: Color(0xFF999999),
),
),
],
),
],
),
);
}
3.7 小結(jié)
通過(guò)上面的一個(gè)例子孝冒,我們成功地把一個(gè)看起來(lái)復(fù)雜的UI界面一步步拆解,將之前提到的組件都用了個(gè)遍拟杉,并且最終得到了不錯(cuò)的效果庄涡。其實(shí),日常開(kāi)發(fā)中90%以上的需求都離不開(kāi)上述提到的基礎(chǔ)組件搬设。因此穴店,只要稍加練習(xí),熟悉了Flutter
中的基礎(chǔ)組件用法拿穴,就已經(jīng)算是邁出了一大步哦~
這里還有銀行卡和朋友圈的UI練習(xí)例子泣洞,由于篇幅原因就不貼代碼了,可以去github倉(cāng)庫(kù)看默色。
4. 總結(jié)
本文首先介紹了Flutter
中構(gòu)建UI界面最常用的基礎(chǔ)組件(容器
球凰,行
,列
腿宰,絕對(duì)定位布局
弟蚀,文本
,圖片
和圖標(biāo)
)用法酗失。接著,介紹了一個(gè)較復(fù)雜的UI實(shí)戰(zhàn)例子昧绣。通過(guò)對(duì)Dom結(jié)構(gòu)的層層拆解规肴,前文提到過(guò)的組件得到一個(gè)綜合運(yùn)用,也算是鞏固了前面所學(xué)的概念知識(shí)。
不過(guò)最后不得不吐槽一句:Flutter
的嵌套真的很難受拖刃。删壮。。如果不對(duì)UI布局進(jìn)行模塊拆分兑牡,那絕對(duì)是噩夢(mèng)般的體驗(yàn)央碟。而且不像web/rn開(kāi)發(fā)樣式可以單獨(dú)抽離,Flutter
這種將樣式當(dāng)做屬性的處理方式均函,一眼看去真的很難理清dom結(jié)構(gòu)亿虽,對(duì)于新接手代碼的開(kāi)發(fā)人員而言,需要費(fèi)點(diǎn)時(shí)間理解苞也。洛勉。。