????這個(gè)章節(jié)本來打算講解Flutter的渲染原理,但是學(xué)習(xí)初期過多的講解原理性的內(nèi)容,并不利于大家快速入門和上手,做出一些帶效果的內(nèi)容;
????所以,我打算換一種思路,先講解一些組件的用法,讓大家習(xí)慣Flutter的開發(fā)過程和模式,再回頭去鞏固原理性的知識(shí);
????另外,在講解這些Widget的時(shí)候,我并不打算將所有的屬性一一列出,因?yàn)闆]有意義,也記不??;
????我后面打算有一個(gè)專題是關(guān)于Flutter布局的,會(huì)選出一些好看的布局界面帶著大家一起來完成:美團(tuán)頁面、京東頁面、B站頁面等等,某些我目前沒有講到的屬性,后面應(yīng)用的會(huì)再進(jìn)行講解
1. 文本W(wǎng)idget
在Android中,我們使用TextView,iOS中我們使用UILabel來顯示文本;
Flutter中,我們使用Text組件控制文本如何展示;
1.1. 普通文本展示
在Flutter中,我們可以將文本的控制顯示分成兩類:
????控制文本布局的參數(shù): 如文本對(duì)齊方式 textAlign、文本排版方向 textDirection,文本顯示最大行數(shù) maxLines、文本截?cái)嘁?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:文本對(duì)齊方式,比如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)容都居中對(duì)齊
? ? ? maxLines: 3, // 顯然 "生。" 被刪除了
? ? ? overflow: TextOverflow.ellipsis, // 超出部分顯示...
//? ? ? textScaleFactor: 1.25,
? ? ? style: TextStyle(
? ? ? ? fontSize: 20,
? ? ? ? color: Colors.purple
? ? ? ),
? ? );
? }
}
1.2. 富文本展示
前面展示的文本,我們都應(yīng)用了相同的樣式,如果我們希望給他們不同的樣式呢?
????比如《定風(fēng)波》我希望字體更大一點(diǎn),并且是黑色字體,并且有加粗效果;
????比如 蘇軾 我希望是紅色字體;
如果希望展示這種混合樣式,那么我們可以利用分片來進(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等
我們直接來對(duì)他們進(jìn)行一個(gè)展示:
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)), // 圓角的實(shí)現(xiàn)
? onPressed: () {
? ? print("同意協(xié)議");
? },
)
事實(shí)上這里還有一個(gè)比較常見的屬性:elevation,用于控制陰影的大小,很多地方都會(huì)有這個(gè)屬性,大家可以自行演示一下
三. 圖片Widget
圖片可以讓我們的應(yīng)用更加豐富多彩,F(xiàn)lutter中使用Image組件
Image組件有很多的構(gòu)造函數(shù),我們這里主要學(xué)習(xí)兩個(gè):
????Image.assets:加載本地資源圖片;
????Image.network:加載網(wǎng)絡(luò)中的圖片;
3.1. 加載網(wǎng)絡(luò)圖片
相對(duì)來講,F(xiàn)lutter中加載網(wǎng)絡(luò)圖片會(huì)更加簡(jiǎn)單,直接傳入U(xiǎn)RL并不需要什么配置,所以我們先來看一下Flutter中如何加載網(wǎng)絡(luò)圖片。
我們先來看看Image有哪些屬性可以設(shè)置:
const Image({
? ...
? this.width, //圖片的寬
? this.height, //圖片高度
? this.color, //圖片的混合色值
? this.colorBlendMode, //混合模式
? this.fit,//縮放模式
? this.alignment = Alignment.center, //對(duì)齊方式
? this.repeat = ImageRepeat.noRepeat, //重復(fù)方式
? ...
})
width、height:用于設(shè)置圖片的寬、高,當(dāng)不指定寬高時(shí),圖片會(huì)根據(jù)當(dāng)前父容器的限制,盡可能的顯示其原始大小,如果只設(shè)置width、height的其中一個(gè),那么另一個(gè)屬性默認(rèn)會(huì)按比例縮放,但可以通過下面介紹的fit屬性來指定適應(yīng)規(guī)則。
fit:該屬性用于在圖片的顯示空間和圖片本身大小不同時(shí)指定圖片的適應(yīng)模式。適應(yīng)模式是在BoxFit中定義,它是一個(gè)枚舉類型,有如下值:
????fill:會(huì)拉伸填充滿顯示空間,圖片本身長(zhǎng)寬比會(huì)發(fā)生變化,圖片會(huì)變形。
????cover:會(huì)按圖片的長(zhǎng)寬比放大后居中填滿顯示空間,圖片不會(huì)變形,超出顯示空間部分會(huì)被剪裁。
????contain:這是圖片的默認(rèn)適應(yīng)規(guī)則,圖片會(huì)在保證圖片本身長(zhǎng)寬比不變的情況下縮放以適應(yīng)當(dāng)前顯示空間,圖片不會(huì)變形。
????fitWidth:圖片的寬度會(huì)縮放到顯示空間的寬度,高度會(huì)按比例縮放,然后居中顯示,圖片不會(huì)變形,超出顯示空間部分會(huì)被剪裁。
????fitHeight:圖片的高度會(huì)縮放到顯示空間的高度,寬度會(huì)按比例縮放,然后居中顯示,圖片不會(huì)變形,超出顯示空間部分會(huì)被剪裁。
????none:圖片沒有適應(yīng)策略,會(huì)在顯示空間內(nèi)顯示圖片,如果圖片比顯示空間大,則顯示空間只會(huì)顯示圖片中間部分。
????color和 colorBlendMode:在圖片繪制時(shí)可以對(duì)每一個(gè)像素進(jìn)行顏色混合處理,color指定混合色,而colorBlendMode指定混合模式;
????repeat:當(dāng)圖片本身大小小于顯示空間時(shí),指定圖片的重復(fù)規(guī)則。
我們對(duì)其中某些屬性做一個(gè)演練:
????注意,這里我用了一個(gè)Container,大家可以把它理解成一個(gè)UIView或者View,就是一個(gè)容器;
????后面我會(huì)專門講到這個(gè)組件的使用;
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. 加載本地圖片
加載本地圖片稍微麻煩一點(diǎn),需要將圖片引入,并且進(jìn)行配置
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. 實(shí)現(xiàn)圓角圖像
在Flutter中實(shí)現(xiàn)圓角效果也是使用一些Widget來實(shí)現(xiàn)的。
3.3.1. 實(shí)現(xiàn)圓角頭像
方式一:CircleAvatar
CircleAvatar可以實(shí)現(xiàn)圓角頭像,也可以添加一個(gè)子Widget:
const CircleAvatar({
? Key key,
? this.child, // 子Widget
? this.backgroundColor, // 背景顏色
? this.backgroundImage, // 背景圖像
? this.foregroundColor, // 前景顏色
? this.radius, // 半徑
? this.minRadius, // 最小半徑
? this.maxRadius, // 最大半徑
})
我們來實(shí)現(xiàn)一個(gè)圓形頭像:
????注意一:這里我們使用的是NetworkImage,因?yàn)閎ackgroundImage要求我們傳入一個(gè)ImageProvider;
????????ImageProvider是一個(gè)抽象類,事實(shí)上所有我們前面創(chuàng)建的Image對(duì)象都有包含image屬性,該屬性就是一個(gè)ImageProvider
????注意二:這里我還在里面添加了一個(gè)文字,但是我在文字外層包裹了一個(gè)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("兵長(zhǎng)利威爾")
? ? ? ? ),
? ? ? ),
? ? );
? }
}
方式二:ClipOval
ClipOval也可以實(shí)現(xiàn)圓角頭像,而且通常是在只有頭像時(shí)使用
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,
? ? ? ? ),
? ? ? ),
? ? );
? }
}
實(shí)現(xiàn)方式三:Container+BoxDecoration
這種方式我們放在講解Container時(shí)來講這種方式
3.3.2. 實(shí)現(xiàn)圓角圖片
方式一:ClipRRect
ClipRRect用于實(shí)現(xiàn)圓角效果,可以設(shè)置圓角的大小。
實(shí)現(xiàn)代碼如下,非常簡(jiǎ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
這個(gè)也放到后面講解Container時(shí)講解
四. 表單Widget
和用戶交互的其中一種就是輸入框,比如注冊(cè)、登錄、搜索,我們收集用戶輸入的內(nèi)容將其提交到服務(wù)器。
4.1. TextField的使用
4.1.1. TextField的介紹
TextField用于接收用戶的文本輸入,它提供了非常多的屬性,我們來看一下源碼:
????但是我們沒必要一個(gè)個(gè)去學(xué)習(xí),很多時(shí)候用到某個(gè)功能時(shí)去查看是否包含某個(gè)屬性即可
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í)幾個(gè)比較常見的屬性:
????一些屬性比較簡(jiǎn)單:keyboardType鍵盤的類型,style設(shè)置樣式,textAlign文本對(duì)齊方式,maxLength最大顯示行數(shù)等等;
decoration:用于設(shè)置輸入框相關(guān)的樣式
????icon:設(shè)置左邊顯示的圖標(biāo)
????labelText:在輸入框上面顯示一個(gè)提示的文本
????hintText:顯示提示的占位文字
????border:輸入框的邊框,默認(rèn)底部有一個(gè)邊框,可以通過InputBorder.none刪除掉
????filled:是否填充輸入框,默認(rèn)為false
????fillColor:輸入框填充的顏色
controller:
onChanged:監(jiān)聽輸入框內(nèi)容的改變,傳入一個(gè)回調(diào)函數(shù)
onSubmitted:點(diǎn)擊鍵盤中右下角的down時(shí),會(huì)回調(diào)的一個(gè)函數(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: "請(qǐng)輸入用戶名",
? ? ? ? border: InputBorder.none,
? ? ? ? filled: true,
? ? ? ? fillColor: Colors.lightGreen
? ? ? ),
? ? ? onChanged: (value) {
? ? ? ? print("onChanged:$value");
? ? ? },
? ? ? onSubmitted: (value) {
? ? ? ? print("onSubmitted:$value");
? ? ? },
? ? );
? }
}
4.1.3. TextField的controller
????我們可以給TextField添加一個(gè)控制器(Controller),可以使用它設(shè)置文本的初始值,也可以使用它來監(jiān)聽文本的改變;
????事實(shí)上,如果我們沒有為TextField提供一個(gè)Controller,那么會(huì)Flutter會(huì)默認(rèn)創(chuàng)建一個(gè)TextEditingController的,這個(gè)結(jié)論可以通過閱讀源碼得到:
@override
? void initState() {
? ? super.initState();
? ? // ...其他代碼
? ? if (widget.controller == null)
? ? ? _controller = TextEditingController();
? }
我們也可以自己來創(chuàng)建一個(gè)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ā)注冊(cè)、登錄頁面時(shí),通常會(huì)有多個(gè)表單需要同時(shí)獲取內(nèi)容或者進(jìn)行一些驗(yàn)證,如果對(duì)每一個(gè)TextField都分別進(jìn)行驗(yàn)證,是一件比較麻煩的事情。
????做過前端的開發(fā)知道,我們可以將多個(gè)input標(biāo)簽放在一個(gè)form里面,F(xiàn)lutter也借鑒了這樣的思想:我們可以通過Form對(duì)輸入框進(jìn)行分組,統(tǒng)一進(jìn)行一些操作。
4.2.1. Form表單的基本使用
Form表單也是一個(gè)Widget,可以在里面放入我們的輸入框。
但是Form表單中輸入框必須是FormField類型的
????我們查看剛剛學(xué)過的TextField是繼承自StatefulWidget,并不是一個(gè)FormField類型;
????我們可以使用TextFormField,它的使用類似于TextField,并且是繼承自FormField的;
我們通過Form的包裹,來實(shí)現(xiàn)一個(gè)注冊(cè)的頁面:
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ī)號(hào)"
? ? ? ? ? ? ),
? ? ? ? ? ),
? ? ? ? ? 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("注 冊(cè)", style: TextStyle(fontSize: 20, color: Colors.white),),
? ? ? ? ? ? ? onPressed: () {
? ? ? ? ? ? ? ? print("點(diǎn)擊了注冊(cè)按鈕");
? ? ? ? ? ? ? },
? ? ? ? ? ? ),
? ? ? ? ? )
? ? ? ? ],
? ? ? ),
? ? );
? }
}
4.2.2. 保存和獲取表單數(shù)據(jù)
有了表單后,我們需要在點(diǎn)擊注冊(cè)時(shí),可以同時(shí)獲取和保存表單中的數(shù)據(jù),怎么可以做到呢?
????1、需要監(jiān)聽注冊(cè)按鈕的點(diǎn)擊,在之前我們已經(jīng)監(jiān)聽的onPressed傳入的回調(diào)中來做即可。(當(dāng)然,如果嵌套太多,我們待會(huì)兒可以將它抽取到一個(gè)單獨(dú)的方法中)
????2、監(jiān)聽到按鈕點(diǎn)擊時(shí),同時(shí)獲取用戶名和密碼的表單信息。
如何同時(shí)獲取用戶名和密碼的表單信息?
????如果我們調(diào)用Form的State對(duì)象的save方法,就會(huì)調(diào)用Form中放入的TextFormField的onSave回調(diào):
TextFormField(
? decoration: InputDecoration(
? ? icon: Icon(Icons.people),
? ? labelText: "用戶名或手機(jī)號(hào)"
? ),
? onSaved: (value) {
? ? print("用戶名:$value");
? },
),
????但是,我們有沒有辦法可以在點(diǎn)擊按鈕時(shí),拿到 Form對(duì)象 來調(diào)用它的save方法呢?
知識(shí)點(diǎn):在Flutter如何可以獲取一個(gè)通過一個(gè)引用獲取一個(gè)StatefulWidget的State對(duì)象呢?
答案:通過綁定一個(gè)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ī)號(hào)"
? ? ? ? ? ? ),
? ? ? ? ? ? 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("注 冊(cè)", style: TextStyle(fontSize: 20, color: Colors.white),),
? ? ? ? ? ? ? onPressed: registerForm,
? ? ? ? ? ? ),
? ? ? ? ? )
? ? ? ? ],
? ? ? ),
? ? );
? }
}
4.2.3. 驗(yàn)證填寫的表單數(shù)據(jù)
在表單中,我們可以添加驗(yàn)證器,如果不符合某些特定的規(guī)則,那么給用戶一定的提示信息
比如我們需要賬號(hào)和密碼有這樣的規(guī)則:賬號(hào)和密碼都不能為空。
按照如下步驟就可以完成整個(gè)驗(yàn)證過程:
????1、為TextFormField添加validator的回調(diào)函數(shù);
????2、調(diào)用Form的State對(duì)象的validate方法,就會(huì)回調(diào)validator傳入的函數(shù);
也可以為TextFormField添加一個(gè)屬性:autovalidate
????不需要調(diào)用validate方法,會(huì)自動(dòng)驗(yàn)證是否符合要求;