Flutter開發(fā)(5)- Flutter UI(2) 基礎(chǔ)Widget

????這個(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)證是否符合要求;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容