概覽
一横殴、文本W(wǎng)idget
二、按鈕Widget
三茬缩、圖片Widget
四赤惊、表單Widget
一、文本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)波》我希望字體更大一點磕秤,并且是黑色字體,并且有加粗效果捧韵;
比如 蘇軾 我希望是紅色字體市咆;
如果希望展示這種混合樣式,那么我們可以利用分片來進(jìn)行操作(在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等
我們直接來對他們進(jìn)行一個展示:
class ButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Column(
children: <Widget>[
FloatingActionButton(
child: Text("FloatingActionButton"),
onPressed: (){
print("FloatingActionButton");
},
),
RaisedButton(
child: Text("RaiseButton"),
onPressed: (){
print("RaiseButton");
},
),
FlatButton(
child: Text("FlatButton"),
onPressed: (){
print("FlatButton");
},
),
OutlineButton(
child: Text("OutlineButton"),
onPressed: (){
print("OutlineButton");
},
),
],
);
}
}
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)),
onPressed: (){
print("同意協(xié)議");
},
)
三、圖片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ò)圖片
我們先來看看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:在圖片繪制時可以對每一個像素進(jìn)行顏色混合處理敏簿,color指定混合色明也,而colorBlendMode指定混合模式;
- repeat:當(dāng)圖片本身大小小于顯示空間時惯裕,指定圖片的重復(fù)規(guī)則温数。
對其中某些屬性做一個演練:
class ImageDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
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 加載本地圖片
加載本地圖片稍微麻煩一點,需要將圖片引入蜻势,并且進(jìn)行配置
class ImageDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
width: 300,
height: 300,
color: Colors.yellow,
child: Image.asset("images/test.jpeg"),
),
);
}
}
四撑刺、表單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)聽
class TextFieldDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFieldPage(),
],
),
);
}
}
class TextFieldPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _TextFieldPageState();
}
}
class _TextFieldPageState extends State<TextFieldPage> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return TextField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "username",
hintText: "請輸入用戶名",
border: InputBorder.none,
filled: true,
fillColor: Colors.lightGreen,
),
onChanged: (value) {
print("onChange$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)容或者進(jìn)行一些驗證酣难,如果對每一個TextField都分別進(jìn)行驗證谍夭,是一件比較麻煩的事情。
做過前端的開發(fā)知道憨募,我們可以將多個input標(biāo)簽放在一個form里面紧索,F(xiàn)lutter也借鑒了這樣的思想:我們可以通過Form對輸入框進(jìn)行分組,統(tǒng)一進(jìn)行一些操作菜谣。
4.2.1Form表單的基本使用
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: "用戶名或手機(jī)號"
),
),
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)然我纪,如果嵌套太多,我們待會兒可以將它抽取到一個單獨(dú)的方法中)
- 2、監(jiān)聽到按鈕點擊時浅悉,同時獲取用戶名和密碼的表單信息趟据。
如何同時獲取用戶名和密碼的表單信息? - 如果我們調(diào)用Form的State對象的save方法术健,就會調(diào)用Form中放入的TextFormField的onSave回調(diào):
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機(jī)號"
),
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: "用戶名或手機(jī)號"
),
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(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機(jī)號",
),
onSaved: (value) {
this.username = value;
},
validator: (value){
if (value.isEmpty) {
return "賬號不能為空";
}
return null;
},
)
void registerForm() {
registerFromKey.currentState.validate();
print("username:$username, password:$password");
}
也可以為TextFormField添加一個屬性:autovalidate
- 不需要調(diào)用validate方法钦无,會自動驗證是否符合要求逗栽;
TextFormField(
obscureText: true,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: "密碼",
),
onSaved: (value) {
this.password = value;
},
validator: (value){
if (value.isEmpty) {
return "密碼不能為空";
}
return null;
},
autovalidate: true,
),