1. 文本W(wǎng)idget
在Android中浇揩,我們使用TextView经伙,iOS中我們使用UILabel來顯示文本翎猛;
Flutter中拘荡,我們使用Text組件控制文本如何展示臼节;
1.1. 普通文本展示
在Flutter中,我們可以將文本的控制顯示分成兩類:
控制文本布局的參數(shù): 如文本對齊方式 textAlign珊皿、文本排版方向 textDirection网缝,文本顯示最大行數(shù) maxLines、文本截斷規(guī)則 overflow 等等蟋定,這些都是構(gòu)造函數(shù)中的參數(shù)粉臊;
控制文本樣式的參數(shù): 如字體名稱 fontFamily、字體大小 fontSize驶兜、文本顏色 color扼仲、文本陰影 shadows 等等,這些參數(shù)被統(tǒng)一封裝到了構(gòu)造函數(shù)中的參數(shù) style 中抄淑;
下面我們來看一下其中一些屬性的使用:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"《定風(fēng)波》 蘇軾 \n莫聽穿林打葉聲屠凶,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬肆资,誰怕矗愧?一蓑煙雨任平生。",
style: TextStyle(
fontSize: 20,
color: Colors.purple
),
);
}
}
展示效果如下:
我們可以通過一些屬性來改變Text的布局:
textAlign:文本對齊方式郑原,比如TextAlign.center
maxLines:最大顯示行數(shù)唉韭,比如1
overflow:超出部分顯示方式夜涕,比如TextOverflow.ellipsis
textScaleFactor:控制文本縮放,比如1.24
代碼如下:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"《定風(fēng)波》 蘇軾 \n莫聽穿林打葉聲属愤,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬春塌,誰怕只壳?一蓑煙雨任平生暑塑。",
textAlign: TextAlign.center, // 所有內(nèi)容都居中對齊
maxLines: 3, // 顯然 "生事格。" 被刪除了
overflow: TextOverflow.ellipsis, // 超出部分顯示...
// textScaleFactor: 1.25,
style: TextStyle(
fontSize: 20,
color: Colors.purple
),
);
}
}
1.2. 富文本展示
前面展示的文本驹愚,我們都應(yīng)用了相同的樣式,如果我們希望給他們不同的樣式呢谁鳍?
比如《定風(fēng)波》我希望字體更大一點劫瞳,并且是黑色字體志于,并且有加粗效果;
比如 蘇軾 我希望是紅色字體养泡;
如果希望展示這種混合樣式奈应,那么我們可以利用分片來進行操作(在Android中钥组,我們可以使用SpannableString,在iOS中点把,我們可以使用NSAttributedString完成,了解即可)
代碼如下:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "《定風(fēng)波》", style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold, color: Colors.black)),
TextSpan(text: "蘇軾", style: TextStyle(fontSize: 18, color: Colors.redAccent)),
TextSpan(text: "\n莫聽穿林打葉聲哥童,何妨吟嘯且徐行褒翰。\n竹杖芒鞋輕勝馬,誰怕朵你?一蓑煙雨任平生抡医。")
],
),
style: TextStyle(fontSize: 20, color: Colors.purple),
textAlign: TextAlign.center,
);
}
}
二. 按鈕Widget
2.1. 按鈕的基礎(chǔ)
Material widget庫中提供了多種按鈕Widget如FloatingActionButton忌傻、RaisedButton水孩、FlatButton琐驴、OutlineButton等
我們直接來對他們進行一個展示:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FloatingActionButton(
child: Text("FloatingActionButton"),
onPressed: () {
print("FloatingActionButton Click");
},
),
RaisedButton(
child: Text("RaisedButton"),
onPressed: () {
print("RaisedButton Click");
},
),
FlatButton(
child: Text("FlatButton"),
onPressed: () {
print("FlatButton Click");
},
),
OutlineButton(
child: Text("OutlineButton"),
onPressed: () {
print("OutlineButton Click");
},
)
],
);
}
}
2.2. 自定義樣式
前面的按鈕我們使用的都是默認(rèn)樣式安疗,我們可以通過一些屬性來改變按鈕的樣式
RaisedButton(
child: Text("同意協(xié)議", style: TextStyle(color: Colors.white)),
color: Colors.orange, // 按鈕的顏色
highlightColor: Colors.orange[700], // 按下去高亮顏色
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), // 圓角的實現(xiàn)
onPressed: () {
print("同意協(xié)議");
},
)
事實上這里還有一個比較常見的屬性:elevation荐类,用于控制陰影的大小茁帽,很多地方都會有這個屬性潘拨,大家可以自行演示一下
三. 圖片Widget
圖片可以讓我們的應(yīng)用更加豐富多彩,F(xiàn)lutter中使用Image組件
Image組件有很多的構(gòu)造函數(shù)铁追,我們這里主要學(xué)習(xí)兩個:
Image.assets:加載本地資源圖片;
Image.network:加載網(wǎng)絡(luò)中的圖片扭屁;
3.1. 加載網(wǎng)絡(luò)圖片
相對來講料滥,F(xiàn)lutter中加載網(wǎng)絡(luò)圖片會更加簡單葵腹,直接傳入URL并不需要什么配置,所以我們先來看一下Flutter中如何加載網(wǎng)絡(luò)圖片鲸匿。
我們先來看看Image有哪些屬性可以設(shè)置:
const Image({
...
this.width, //圖片的寬
this.height, //圖片高度
this.color, //圖片的混合色值
this.colorBlendMode, //混合模式
this.fit,//縮放模式
this.alignment = Alignment.center, //對齊方式
this.repeat = ImageRepeat.noRepeat, //重復(fù)方式
...
})
width
、height
:用于設(shè)置圖片的寬磺浙、高徒坡,當(dāng)不指定寬高時喇完,圖片會根據(jù)當(dāng)前父容器的限制锦溪,盡可能的顯示其原始大小,如果只設(shè)置width
防楷、height
的其中一個复局,那么另一個屬性默認(rèn)會按比例縮放粟判,但可以通過下面介紹的fit
屬性來指定適應(yīng)規(guī)則档礁。fit
:該屬性用于在圖片的顯示空間和圖片本身大小不同時指定圖片的適應(yīng)模式。適應(yīng)模式是在BoxFit
中定義野舶,它是一個枚舉類型宰衙,有如下值:fill
:會拉伸填充滿顯示空間,圖片本身長寬比會發(fā)生變化一屋,圖片會變形冀墨。cover
:會按圖片的長寬比放大后居中填滿顯示空間诽嘉,圖片不會變形虫腋,超出顯示空間部分會被剪裁稀余。contain
:這是圖片的默認(rèn)適應(yīng)規(guī)則睛琳,圖片會在保證圖片本身長寬比不變的情況下縮放以適應(yīng)當(dāng)前顯示空間师骗,圖片不會變形辟癌。fitWidth
:圖片的寬度會縮放到顯示空間的寬度愿待,高度會按比例縮放要出,然后居中顯示患蹂,圖片不會變形传于,超出顯示空間部分會被剪裁沼溜。fitHeight
:圖片的高度會縮放到顯示空間的高度系草,寬度會按比例縮放找都,然后居中顯示能耻,圖片不會變形晓猛,超出顯示空間部分會被剪裁诫睬。none
:圖片沒有適應(yīng)策略,會在顯示空間內(nèi)顯示圖片蚓曼,如果圖片比顯示空間大,則顯示空間只會顯示圖片中間部分其弊。color
和colorBlendMode
:在圖片繪制時可以對每一個像素進行顏色混合處理梭伐,color
指定混合色,而colorBlendMode
指定混合模式赂苗;repeat
:當(dāng)圖片本身大小小于顯示空間時拌滋,指定圖片的重復(fù)規(guī)則鸠真。
我們對其中某些屬性做一個演練:
注意吠卷,這里我用了一個Container货岭,大家可以把它理解成一個UIView或者View,就是一個容器搔谴;
后面我會專門講到這個組件的使用;
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Image.network(
"http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",
alignment: Alignment.topCenter,
repeat: ImageRepeat.repeatY,
color: Colors.red,
colorBlendMode: BlendMode.colorDodge,
),
width: 300,
height: 300,
color: Colors.yellow,
),
);
}
}
3.2. 加載本地圖片
加載本地圖片稍微麻煩一點融师,需要將圖片引入旱爆,并且進行配置
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
height: 300,
color: Colors.yellow,
child: Image.asset("images/test.jpeg"),
),
);
}
}
3.3. 實現(xiàn)圓角圖像
在Flutter中實現(xiàn)圓角效果也是使用一些Widget來實現(xiàn)的。
3.3.1. 實現(xiàn)圓角頭像
方式一:CircleAvatar
CircleAvatar可以實現(xiàn)圓角頭像浩淘,也可以添加一個子Widget:
const CircleAvatar({
Key key,
this.child, // 子Widget
this.backgroundColor, // 背景顏色
this.backgroundImage, // 背景圖像
this.foregroundColor, // 前景顏色
this.radius, // 半徑
this.minRadius, // 最小半徑
this.maxRadius, // 最大半徑
})
我們來實現(xiàn)一個圓形頭像:
注意一:這里我們使用的是NetworkImage,因為backgroundImage要求我們傳入一個ImageProvider左驾;
ImageProvider是一個抽象類,事實上所有我們前面創(chuàng)建的Image對象都有包含
image
屬性帆吻,該屬性就是一個ImageProvider注意二:這里我還在里面添加了一個文字王带,但是我在文字外層包裹了一個Container愕撰;
這里Container的作用是為了可以控制文字在其中的位置調(diào)整;
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: CircleAvatar(
radius: 100,
backgroundImage: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
child: Container(
alignment: Alignment(0, .5),
width: 200,
height: 200,
child: Text("兵長利威爾")
),
),
);
}
}
方式二:ClipOval
ClipOval也可以實現(xiàn)圓角頭像醋寝,而且通常是在只有頭像時使用
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ClipOval(
child: Image.network(
"https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
width: 200,
height: 200,
),
),
);
}
}
實現(xiàn)方式三:Container+BoxDecoration
這種方式我們放在講解Container時來講這種方式
3.3.2. 實現(xiàn)圓角圖片
方式一:ClipRRect
ClipRRect用于實現(xiàn)圓角效果搞挣,可以設(shè)置圓角的大小。
實現(xiàn)代碼如下甥桂,非常簡單:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
"https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
width: 200,
height: 200,
),
),
);
}
}
方式二:Container+BoxDecoration
這個也放到后面講解Container時講解
四. 表單Widget
和用戶交互的其中一種就是輸入框柿究,比如注冊邮旷、登錄黄选、搜索,我們收集用戶輸入的內(nèi)容將其提交到服務(wù)器婶肩。
4.1. TextField的使用
4.1.1. TextField的介紹
TextField用于接收用戶的文本輸入办陷,它提供了非常多的屬性,我們來看一下源碼:
- 但是我們沒必要一個個去學(xué)習(xí)律歼,很多時候用到某個功能時去查看是否包含某個屬性即可
const TextField({
Key key,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.strutStyle,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
ToolbarOptions toolbarOptions,
this.showCursor,
this.autofocus = false,
this.obscureText = false,
this.autocorrect = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforced = true,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.onTap,
this.buildCounter,
this.scrollController,
this.scrollPhysics,
})
我們來學(xué)習(xí)幾個比較常見的屬性:
一些屬性比較簡單:
keyboardType
鍵盤的類型民镜,style
設(shè)置樣式,textAlign
文本對齊方式险毁,maxLength
最大顯示行數(shù)等等制圈;decoration
:用于設(shè)置輸入框相關(guān)的樣式icon:設(shè)置左邊顯示的圖標(biāo)
labelText:在輸入框上面顯示一個提示的文本
hintText:顯示提示的占位文字
border:輸入框的邊框们童,默認(rèn)底部有一個邊框,可以通過InputBorder.none刪除掉
filled:是否填充輸入框鲸鹦,默認(rèn)為false
fillColor:輸入框填充的顏色
controller
:onChanged
:監(jiān)聽輸入框內(nèi)容的改變慧库,傳入一個回調(diào)函數(shù)onSubmitted
:點擊鍵盤中右下角的down時,會回調(diào)的一個函數(shù)
4.1.2. TextField的樣式以及監(jiān)聽
我們來演示一下TextField的decoration屬性以及監(jiān)聽:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFieldDemo()
],
),
);
}
}
class TextFieldDemo extends StatefulWidget {
@override
_TextFieldDemoState createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State<TextFieldDemo> {
@override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "username",
hintText: "請輸入用戶名",
border: InputBorder.none,
filled: true,
fillColor: Colors.lightGreen
),
onChanged: (value) {
print("onChanged:$value");
},
onSubmitted: (value) {
print("onSubmitted:$value");
},
);
}
}
4.1.3. TextField的controller
我們可以給TextField添加一個控制器(Controller)馋嗜,可以使用它設(shè)置文本的初始值齐板,也可以使用它來監(jiān)聽文本的改變;
事實上葛菇,如果我們沒有為TextField提供一個Controller甘磨,那么Flutter會默認(rèn)創(chuàng)建一個TextEditingController的,這個結(jié)論可以通過閱讀源碼得到:
@override
void initState() {
super.initState();
// ...其他代碼
if (widget.controller == null)
_controller = TextEditingController();
}
我們也可以自己來創(chuàng)建一個Controller控制一些內(nèi)容:
class _TextFieldDemoState extends State<TextFieldDemo> {
final textEditingController = TextEditingController();
@override
void initState() {
super.initState();
// 1.設(shè)置默認(rèn)值
textEditingController.text = "Hello World";
// 2.監(jiān)聽文本框
textEditingController.addListener(() {
print("textEditingController:${textEditingController.text}");
});
}
// ...省略build方法
}
4.2. Form表單的使用
在我們開發(fā)注冊眯停、登錄頁面時济舆,通常會有多個表單需要同時獲取內(nèi)容或者進行一些驗證,如果對每一個TextField都分別進行驗證莺债,是一件比較麻煩的事情吗冤。
做過前端的開發(fā)知道,我們可以將多個input標(biāo)簽放在一個form里面九府,F(xiàn)lutter也借鑒了這樣的思想:我們可以通過Form對輸入框進行分組椎瘟,統(tǒng)一進行一些操作。
4.2.1. Form表單的基本使用
Form表單也是一個Widget侄旬,可以在里面放入我們的輸入框肺蔚。
但是Form表單中輸入框必須是FormField類型的
我們查看剛剛學(xué)過的TextField是繼承自StatefulWidget,并不是一個FormField類型儡羔;
我們可以使用TextFormField宣羊,它的使用類似于TextField,并且是繼承自FormField的汰蜘;
我們通過Form的包裹仇冯,來實現(xiàn)一個注冊的頁面:
class FormDemo extends StatefulWidget {
@override
_FormDemoState createState() => _FormDemoState();
}
class _FormDemoState extends State<FormDemo> {
@override
Widget build(BuildContext context) {
return Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
),
TextFormField(
obscureText: true,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: "密碼"
),
),
SizedBox(height: 16,),
Container(
width: double.infinity,
height: 44,
child: RaisedButton(
color: Colors.lightGreen,
child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
onPressed: () {
print("點擊了注冊按鈕");
},
),
)
],
),
);
}
}
4.2.2. 保存和獲取表單數(shù)據(jù)
有了表單后,我們需要在點擊注冊時族操,可以同時獲取和保存表單中的數(shù)據(jù)苛坚,怎么可以做到呢?
1色难、需要監(jiān)聽注冊按鈕的點擊泼舱,在之前我們已經(jīng)監(jiān)聽的onPressed傳入的回調(diào)中來做即可。(當(dāng)然枷莉,如果嵌套太多娇昙,我們待會兒可以將它抽取到一個單獨的方法中)
2、監(jiān)聽到按鈕點擊時笤妙,同時獲取
用戶名
和密碼
的表單信息冒掌。
如何同時獲取用戶名
和密碼
的表單信息噪裕?
- 如果我們調(diào)用
Form的State對象
的save方法,就會調(diào)用Form中放入的TextFormField的onSave回調(diào):
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
onSaved: (value) {
print("用戶名:$value");
},
),
- 但是股毫,我們有沒有辦法可以在點擊按鈕時州疾,拿到
Form對象
來調(diào)用它的save方法呢?
知識點:在Flutter如何可以獲取一個通過一個引用獲取一個StatefulWidget的State對象呢皇拣?
答案:通過綁定一個GlobalKey即可严蓖。
案例代碼演練:
class FormDemo extends StatefulWidget {
@override
_FormDemoState createState() => _FormDemoState();
}
class _FormDemoState extends State<FormDemo> {
final registerFormKey = GlobalKey<FormState>();
String username, password;
void registerForm() {
registerFormKey.currentState.save();
print("username:$username password:$password");
}
@override
Widget build(BuildContext context) {
return Form(
key: registerFormKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
onSaved: (value) {
this.username = value;
},
),
TextFormField(
obscureText: true,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: "密碼"
),
onSaved: (value) {
this.password = value;
},
),
SizedBox(height: 16,),
Container(
width: double.infinity,
height: 44,
child: RaisedButton(
color: Colors.lightGreen,
child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
onPressed: registerForm,
),
)
],
),
);
}
}
4.2.3. 驗證填寫的表單數(shù)據(jù)
在表單中,我們可以添加驗證器
氧急,如果不符合某些特定的規(guī)則颗胡,那么給用戶一定的提示信息
比如我們需要賬號和密碼有這樣的規(guī)則:賬號和密碼都不能為空。
按照如下步驟就可以完成整個驗證過程:
1吩坝、為TextFormField添加validator的回調(diào)函數(shù)毒姨;
2、調(diào)用Form的State對象的validate方法钉寝,就會回調(diào)validator傳入的函數(shù)弧呐;
也可以為TextFormField添加一個屬性:autovalidate
- 不需要調(diào)用validate方法,會自動驗證是否符合要求嵌纲;