在Flutter中蘸朋,幾乎所有的對象都是一個(gè)Widget
邑茄,與原生開發(fā)中的控件不同的是艾帐,F(xiàn)lutter中的widget
的概念更廣泛荡陷,它不僅可以表示UI元素换衬,也可以表示一些功能性的組件如:用于手勢檢測的 GestureDetector
widget途戒、用于應(yīng)用主題數(shù)據(jù)傳遞的Theme
等等愈魏。由于Flutter主要就是用于構(gòu)建用戶界面的搭综,所以筹淫,在大多數(shù)時(shí)候站辉,可以認(rèn)為widget就是一個(gè)控件,不必糾結(jié)于概念损姜。
Widget的功能是“描述一個(gè)UI元素的配置數(shù)據(jù)”饰剥,Widget其實(shí)并不是表示最終繪制在設(shè)備屏幕上的顯示元素,而只是顯示元素的一個(gè)配置數(shù)據(jù)摧阅。實(shí)際上汰蓉,F(xiàn)lutter中真正代表屏幕上顯示元素的類是
Element
,也就是說Widget只是描述Element
的一個(gè)配置棒卷。一個(gè)Widget可以對應(yīng)多個(gè)Element
顾孽,這是因?yàn)橥粋€(gè)Widget對象可以被添加到UI樹的不同部分祝钢,而真正渲染時(shí),UI樹的每一個(gè)節(jié)點(diǎn)都會(huì)對應(yīng)一個(gè)Element
對象若厚。
Widget
StatelessWidget
和StatefulWidget
是flutter
的基礎(chǔ)組件拦英,日常開發(fā)中自定義Widget
都是選擇繼承這兩者之一。也是在往后的開放中测秸,我們最多接觸的Widget:
StatelessWidget
:無狀態(tài)的疤估,展示信息,面向那些始終不變的UI控件乞封;StatefulWidget
:有狀態(tài)的做裙,可以通過改變狀態(tài)使得 UI 發(fā)生變化,可以包含用戶交互(比如彈出一個(gè) dialog)肃晚。
在實(shí)際使用中锚贱,Stateless與Stateful的選擇需要取決于這個(gè) Widget 是有狀態(tài)還是無狀態(tài),簡單來說看界面是否需要更新关串。
Stateless Widget
StatelessWidget用于不需要維護(hù)狀態(tài)的場景拧廊,它通常在
build
方法中通過嵌套其它Widget來構(gòu)建UI,在構(gòu)建過程中會(huì)遞歸的構(gòu)建其嵌套的Widget晋修。
BuildContext
表示構(gòu)建widget的上下文吧碾,它是操作widget在樹中位置的一個(gè)句柄,它包含了一些查找墓卦、遍歷當(dāng)前Widget樹的一些方法倦春。每一個(gè)widget都有一個(gè)自己的context對象。
import 'package:flutter/material.dart';
void main() => runApp(StatelessApp());
class StatelessApp extends StatelessWidget {
///在build方法中通過嵌套其它Widget來構(gòu)建UI落剪,在構(gòu)建過程中會(huì)遞歸的構(gòu)建其嵌套的Widget
@override
Widget build(BuildContext context) {
//嵌套 MaterialApp:封裝了應(yīng)用程序?qū)崿F(xiàn)Material Design所需要的一些widget
return MaterialApp(
title: "Widget演示", //標(biāo)題,顯示在recent時(shí)候的標(biāo)題
//主頁面
//Scaffold : Material Design布局結(jié)構(gòu)的基本實(shí)現(xiàn)睁本。
home: Scaffold(
//ToolBar/ActionBar
appBar: AppBar(title: Text("Widget")),
body: Text("Hello,Flutter!"),
)
);
}
}
Material Design:
一種設(shè)計(jì)語言,Material Design 于2014年的 Google I/O 首次亮相忠怖,是谷歌推出的全新的設(shè)計(jì)語言呢堰。說白了,就是一種設(shè)計(jì)風(fēng)格凡泣。
Stateful Widget
StatefulWidget是動(dòng)態(tài)的枉疼,添加了一個(gè)新的接口
createState()
用于創(chuàng)建和Stateful widget相關(guān)的狀態(tài)State
,它在Stateful widget的生命周期中可能會(huì)被多次調(diào)用鞋拟。
當(dāng)State被改變時(shí)骂维,可以手動(dòng)調(diào)用其
setState()
方法通知Flutter framework狀態(tài)發(fā)生改變,F(xiàn)lutter framework在收到消息后贺纲,會(huì)重新調(diào)用其build
方法重新構(gòu)建widget樹航闺,從而達(dá)到更新UI的目的。
class StatefulState extends State<StatefulApp> {
int _i;
///當(dāng)Widget第一次插入到Widget樹時(shí)會(huì)被調(diào)用哮笆,對于每一個(gè)State對象来颤,F(xiàn)lutter framework只會(huì)調(diào)用一次該回調(diào)
@override
void initState() {
super.initState();
_i = 1;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Widget演示",
theme: ThemeData(),
home: Scaffold(
appBar: AppBar(title: Text("Widget")),
body: RaisedButton(
onPressed: () {
//修改狀態(tài),setState會(huì)重新調(diào)用build更新ui
setState(() {
_i++;
});
},
child: Text("Hello,Flutter! $_i"),
),
));
}
}
State生命周期
State類除了
build
之外還提供了很多方法能夠讓我們重寫稠肘,這些方法會(huì)在不同的狀態(tài)下由Flutter調(diào)起執(zhí)行福铅,所以這些方法我們就稱之為生命周期方法。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isShowChild;
///當(dāng)Widget第一次插入到Widget樹時(shí)會(huì)被調(diào)用项阴,對于每一個(gè)State對象滑黔,F(xiàn)lutter framework只會(huì)調(diào)用一次該回調(diào)
@override
void initState() {
super.initState();
isShowChild = true;
debugPrint("parent initState......");
}
///初始化時(shí),在initState()之后立刻調(diào)用
///當(dāng)依賴的InheritedWidget rebuild,會(huì)觸發(fā)此接口被調(diào)用
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("parent didChangeDependencies......");
}
///繪制界面环揽,當(dāng)setState觸發(fā)的時(shí)候會(huì)再次被調(diào)用
@override
Widget build(BuildContext context) {
debugPrint("parent build......");
return MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
setState(() {
isShowChild = !isShowChild;
});
},
child: isShowChild ? Child() : Text("演示移除Child"),
)),
),
);
}
///狀態(tài)改變的時(shí)候會(huì)調(diào)用該方法,比如調(diào)用了setState
@override
void didUpdateWidget(MyApp oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint("parent didUpdateWidget......");
}
///當(dāng)State對象從樹中被移除時(shí)略荡,會(huì)調(diào)用此回調(diào)
@override
void deactivate() {
super.deactivate();
debugPrint('parent deactivate......');
}
///當(dāng)State對象從樹中被永久移除時(shí)調(diào)用;通常在此回調(diào)中釋放資源
@override
void dispose() {
super.dispose();
debugPrint('parent dispose......');
}
}
class Child extends StatefulWidget {
@override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
@override
Widget build(BuildContext context) {
debugPrint("child build......");
return Text('lifeCycle');
}
@override
void initState() {
super.initState();
debugPrint("child initState......");
}
///初始化時(shí)歉胶,在initState()之后立刻調(diào)用
///當(dāng)依賴的InheritedWidget rebuild,會(huì)觸發(fā)此接口被調(diào)用
@override
void didChangeDependencies() {
super.didChangeDependencies();
debugPrint("child didChangeDependencies......");
}
///父widget狀態(tài)改變的時(shí)候會(huì)調(diào)用該方法,比如父節(jié)點(diǎn)調(diào)用了setState
@override
void didUpdateWidget(Child oldWidget) {
super.didUpdateWidget(oldWidget);
debugPrint("child didUpdateWidget......");
}
///當(dāng)State對象從樹中被移除時(shí)汛兜,會(huì)調(diào)用此回調(diào)
@override
void deactivate() {
super.deactivate();
debugPrint('child deactivate......');
}
///當(dāng)State對象從樹中被永久移除時(shí)調(diào)用;通常在此回調(diào)中釋放資源
@override
void dispose() {
super.dispose();
debugPrint('child dispose......');
}
}
執(zhí)行的輸出結(jié)果顯示為:
- 運(yùn)行到顯示
I/flutter (22218): parent initState......
I/flutter (22218): parent didChangeDependencies......
I/flutter (22218): parent build......
I/flutter (22218): child initState......
I/flutter (22218): child didChangeDependencies......
I/flutter (22218): child build......
- 點(diǎn)擊按鈕會(huì)移除Child
I/flutter (22218): parent build......
I/flutter (22218): child deactivate......
I/flutter (22218): child dispose......
- 將MyApp的代碼由
child: isShowChild ? Child() : Text("演示移除Child")
通今,改為child: Child()
粥谬,點(diǎn)擊按鈕時(shí)
I/flutter (22765): parent build......
I/flutter (22765): child didUpdateWidget......
I/flutter (22765): child build......
從這些實(shí)驗(yàn)中能夠得出State的生命周期為:
基礎(chǔ)widget
文本顯示
Text
Text
是展示單一格式的文本W(wǎng)idget(Android TextView
)。
import 'package:flutter/material.dart';
///
/// main方法 調(diào)用runApp傳遞Widget辫塌,這個(gè)Widget成為widget樹的根
void main() => runApp(TextApp());
///
/// 1漏策、單一文本Text
///
//創(chuàng)建一個(gè)無狀態(tài)的Widget
class TextApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//封裝了應(yīng)用程序?qū)崿F(xiàn)Material Design所需要的一些widget
return MaterialApp(
title: "Text演示", //標(biāo)題,顯示在recent時(shí)候的標(biāo)題
//主頁面
//Scaffold : Material Design布局結(jié)構(gòu)的基本實(shí)現(xiàn)。
home: Scaffold(
//ToolBar/ActionBar
appBar: AppBar(title: Text("Text")),
body: Text("Hello,Flutter"),
),
);
}
}
在使用Text
顯示文字時(shí)候臼氨,可能需要對文字設(shè)置各種不同的樣式掺喻,類似Android的 android:textColor/Size
等
在Flutter中也擁有類似的屬性
Widget _TextBody() {
return Text(
"Hello,Flutter",
style: TextStyle(
//顏色
color: Colors.red,
//字號 默認(rèn)14
fontSize: 18,
//粗細(xì)
fontWeight: FontWeight.w800,
//斜體
fontStyle: FontStyle.italic,
//underline:下劃線,overline:上劃線储矩,lineThrough:刪除線
decoration: TextDecoration.lineThrough,
decorationColor: Colors.black,
//solid:實(shí)線感耙,double:雙線,dotted:點(diǎn)虛線椰苟,dashed:橫虛線抑月,wavy:波浪線
decorationStyle: TextDecorationStyle.wavy),
);
}
class TextApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Text演示",
home: Scaffold(
appBar: AppBar(title: Text("Text")),
body: _TextBody(),
),
);
}
}
RichText
如果需要顯示更為豐富樣式的文本(比如一段文本中文字不同顏色),可以使用RichText
或者Text.rich
Widget _RichTextBody() {
var textSpan = TextSpan(
text: "Hello",
style: TextStyle(color: Colors.red),
children: [
TextSpan(text: "Flu", style: TextStyle(color: Colors.blue)),
TextSpan(text: "uter", style: TextStyle(color: Colors.yellow)),
],
);
//Text.rich(textSpan);
return RichText(text: textSpan);
}
DefaultTextStyle
在widget樹中,文本的樣式默認(rèn)是可以被繼承的舆蝴,因此谦絮,如果在widget樹的某一個(gè)節(jié)點(diǎn)處設(shè)置一個(gè)默認(rèn)的文本樣式,那么該節(jié)點(diǎn)的子樹中所有文本都會(huì)默認(rèn)使用這個(gè)樣式洁仗。相當(dāng)于在Android中定義 Theme
Widget _DefaultStyle(){
DefaultTextStyle(
//設(shè)置文本默認(rèn)樣式
style: TextStyle(
color:Colors.red,
fontSize: 20.0,
),
textAlign: TextAlign.start,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Hello Flutter!"),
Text("Hello Flutter!"),
Text("Hello Flutter!",
style: TextStyle(
inherit: false, //不繼承默認(rèn)樣式
color: Colors.grey
),
),
],
),
);
}
圖片顯示
"圖文":有文字顯示W(wǎng)idget层皱,又怎么少的了圖片呢。
FlutterLogo
這個(gè)Widget用于顯示Flutter的logo......
Widget flutterLogo() {
return FlutterLogo(
//大小
size: 100,
//logo顏色 默認(rèn)為 Colors.blue
colors: Colors.red,
//markOnly:只顯示logo赠潦,horizontal:logo右邊顯示flutter文字叫胖,stacked:logo下面顯示文字
style: FlutterLogoStyle.stacked,
//logo上文字顏色
textColor: Colors.blue,
);
}
Icon
主要用于顯示內(nèi)置圖標(biāo)的Widget
Widget icon() {
return Icon(
//使用預(yù)定義Material icons
// https://docs.flutter.io/flutter/material/Icons-class.html
Icons.add,
size: 100,
color: Colors.red);
}
Image
顯示圖片的Widget
。圖片常用的格式主要有bmp,jpg,png,gif,webp等她奥,Android中并不是天生支持gif和webp動(dòng)圖瓮增,但是這一特性在flutter中被很好的支持了怎棱。
方式 | 解釋 |
---|---|
Image() | 使用ImageProvider提供圖片,如下方法本質(zhì)上也是使用的這個(gè)方法 |
Image.asset | 加載資源圖片 |
Image.file | 加載本地圖片文件 |
Image.network | 加載網(wǎng)絡(luò)圖片 |
Image.memory | 加載內(nèi)存圖片 |
Iamge.asset
在工程目錄下創(chuàng)建目錄绷跑,如:assets拳恋,將圖片放入此目錄。打開項(xiàng)目根目錄:pubspec.yaml
return MaterialApp(
title: "Image演示",
home: Scaffold(
appBar: AppBar(title: Text("Image")),
body: Image.asset("assets/banner.jpeg"),
),
);
Image.file
在sd卡中放入一張圖片砸捏。然后利用path_provider庫獲取sd卡根目錄(Dart庫版本可以在:https://pub.dartlang.org/packages查詢)谬运。
注意權(quán)限
class ImageState extends State<ImageApp> {
Image image;
@override
void initState() {
super.initState();
getExternalStorageDirectory().then((path) {
setState(() {
image = Image.file(File("${path.path}${Platform.pathSeparator}banner.jpeg"));
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Image演示",
home: Scaffold(
appBar: AppBar(title: Text("Image")),
body: image,
),
);
}
}
Image.network
直接給網(wǎng)絡(luò)地址即可。
Flutter 1.0垦藏,加載https時(shí)候經(jīng)常出現(xiàn)證書錯(cuò)誤梆暖。必須斷開AS打開app
Image.memory
Future<List<int>> _imageByte() async {
String path = (await getExternalStorageDirectory()).path;
return await File("$path${Platform.pathSeparator}banner.jpeg").readAsBytes();
}
class ImageState extends State<ImageApp> {
Image image;
@override
void initState() {
super.initState();
_imageByte().then((bytes) {
setState(() {
image = Image.memory(bytes);
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Image演示",
home: Scaffold(
appBar: AppBar(title: Text("Image")),
body: image,
),
);
}
}
fit屬性相當(dāng)于android中的scaletype,定義如下:
fit | 說明 | 效果 |
---|---|---|
BoxFit.fill | 填充掂骏,忽略原有的寬高比轰驳,填滿為止 | 基礎(chǔ)Widget_Image_Fill.png
|
BoxFit.contain | 包含,不改變原有比例讓容器包含整個(gè)圖片芭挽,容器多余部分填充背景 | 基礎(chǔ)Widget_Image_Contain.png
|
BoxFit.cover | 覆蓋滑废,不改變原有比例,讓圖片充滿整個(gè)容器袜爪,圖片多余部分裁剪 | 基礎(chǔ)Widget_Image_Cover.png
|
BoxFit.fitWidth | 橫向圖片填充 | 基礎(chǔ)Widget_Image_FitWidth.png
|
BoxFit.fitHeight | 縱向圖片填充 | 基礎(chǔ)Widget_Image_FitHeight.png
|
BoxFit.none | 原始大小居中 | 基礎(chǔ)Widget_Image_none.png
|
BoxFit.scaleDown | 圖片大小小于容器事相當(dāng)于none蠕趁,圖片大小大于容器時(shí)縮小圖片大小實(shí)現(xiàn)contain | 基礎(chǔ)Widget_Image_ScaleDown.png
|
CircleAvatar
主要用來顯示用戶的頭像,任何圖片都會(huì)被剪切為圓形辛馆。
CircleAvatar(
//圖片提供者 ImageProvider
backgroundImage: AssetImage("assets/banner.jpeg"),
//半徑俺陋,控制大小
radius: 50.0,
);
FadeInImage
當(dāng)使用默認(rèn)Image
widget顯示圖片時(shí),您可能會(huì)注意到它們在加載完成后會(huì)直接顯示到屏幕上昙篙。這可能會(huì)讓用戶產(chǎn)生視覺突兀腊状。如果最初顯示一個(gè)占位符,然后在圖像加載完顯示時(shí)淡入苔可,我們可以使用FadeInImage
來達(dá)到這個(gè)目的缴挖!
image = FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: 'https://flutter.io/images/homepage/header-illustration.png',
);
按鈕
Material widget庫中提供了多種按鈕Widget如RaisedButton、FlatButton焚辅、OutlineButton等映屋,它們都是直接或間接對RawMaterialButton的包裝定制,所以他們大多數(shù)屬性都和RawMaterialButton
一樣同蜻。所有Material 庫中的按鈕都有如下相同點(diǎn):
- 按下時(shí)都會(huì)有“水波動(dòng)畫”棚点。
- 有一個(gè)
onPressed
屬性來設(shè)置點(diǎn)擊回調(diào),當(dāng)按鈕按下時(shí)會(huì)執(zhí)行該回調(diào)湾蔓,如果不提供該回調(diào)則按鈕會(huì)處于禁用狀態(tài)瘫析,禁用狀態(tài)不響應(yīng)用戶點(diǎn)擊。
RaisedButton
"漂浮"按鈕,它默認(rèn)帶有陰影和灰色背景
RaisedButton(
child: Text("normal"),
onPressed: () => {},
)
FlatButton
扁平按鈕贬循,默認(rèn)背景透明并不帶陰影
FlatButton(
child: Text("normal"),
onPressed: () => {},
)
OutlineButton
默認(rèn)有一個(gè)邊框咸包,不帶陰影且背景透明。
OutlineButton(
child: Text("normal"),
onPressed: () => {},
)
IconButton
可點(diǎn)擊的Icon
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () => {},
)
按鈕外觀可以通過其屬性來定義杖虾,不同按鈕屬性大同小異
const FlatButton({
...
@required this.onPressed, //按鈕點(diǎn)擊回調(diào)
this.textColor, //按鈕文字顏色
this.disabledTextColor, //按鈕禁用時(shí)的文字顏色
this.color, //按鈕背景顏色
this.disabledColor,//按鈕禁用時(shí)的背景顏色
this.highlightColor, //按鈕按下時(shí)的背景顏色
this.splashColor, //點(diǎn)擊時(shí)诉儒,水波動(dòng)畫中水波的顏色
this.colorBrightness,//按鈕主題,默認(rèn)是淺色主題
this.padding, //按鈕的填充
this.shape, //外形
@required this.child, //按鈕的內(nèi)容
})
FlatButton(
onPressed: () => {},
child: Text("Raised"),
//藍(lán)色
color: Colors.blue,
//水波
splashColor: Colors.yellow,
//深色主題亏掀,這樣文字顏色會(huì)變成白色
colorBrightness: Brightness.dark,
//圓角按鈕
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
)
而RaisedButton
,默認(rèn)配置有陰影泛释,因此在配置RaisedButton
時(shí)滤愕,擁有一系列 elevation 屬性的配置
const RaisedButton({
...
this.elevation = 2.0, //正常狀態(tài)下的陰影
this.highlightElevation = 8.0,//按下時(shí)的陰影
this.disabledElevation = 0.0,// 禁用時(shí)的陰影
...
}
輸入框
import 'package:flutter/material.dart';
void main() => runApp(Demo1());
class Demo1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Demo1",
home: Scaffold(
appBar: AppBar(
title: Text("登錄"),
),
//線性布局,垂直方向
body: Column(
children: <Widget>[
TextField(
//自動(dòng)獲得焦點(diǎn)
autofocus: true,
decoration: InputDecoration(
labelText: "用戶名",
hintText: "用戶名或郵箱",
prefixIcon: Icon(Icons.person)),
),
TextField(
//隱藏正在編輯的文本
obscureText: true,
decoration: InputDecoration(
labelText: "密碼",
hintText: "您的登錄密碼",
prefixIcon: Icon(Icons.lock)),
),
],
),
),
);
}
}
這個(gè)效果非常的“系統(tǒng)”怜校,我們可能大多數(shù)情況下需要將下劃線更換為矩形邊框间影,這時(shí)候可能就需要組合widget來完成:
//容器 設(shè)置一個(gè)控件的尺寸、背景茄茁、margin
Container(
margin: EdgeInsets.all(32),
child: TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "用戶名",
hintText: "用戶名或郵箱",
prefixIcon: Icon(Icons.person),
border: InputBorder.none //隱藏下劃線
)),
//裝飾
decoration: BoxDecoration(
// 邊框淺灰色魂贬,寬度1像素
border: Border.all(color: Colors.red[200], width: 1.0),
//圓角
borderRadius: BorderRadius.circular(5.0),
),
)
焦點(diǎn)控制
**FocusNode**: 與Widget綁定,代表了這個(gè)Widget的焦點(diǎn)
**FocusScope**: 焦點(diǎn)控制范圍
**FocusScopeNode**:控制焦點(diǎn)
class _TextFocusState extends State<TextFocusWidget> {
FocusNode focusNode1 = new FocusNode();
FocusNode focusNode2 = new FocusNode();
void _listener() {
debugPrint("用戶名輸入框焦點(diǎn):${focusNode1.hasFocus}");
}
@override
void initState() {
super.initState();
//監(jiān)聽焦點(diǎn)狀態(tài)改變事件
focusNode1.addListener(_listener);
}
@override
void dispose() {
super.dispose();
focusNode1.dispose();
focusNode2.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextField(
autofocus: true,
//關(guān)聯(lián)焦點(diǎn)
focusNode: focusNode1,
//設(shè)置鍵盤動(dòng)作為: 下一步
textInputAction: TextInputAction.next,
//點(diǎn)擊下一步執(zhí)行回調(diào)
onEditingComplete: () {
//獲得 context對應(yīng)UI樹的焦點(diǎn)范圍 的焦點(diǎn)控制器
FocusScopeNode focusScopeNode = FocusScope.of(context);
//將焦點(diǎn)交給focusNode2
focusScopeNode.requestFocus(focusNode2);
},
decoration: InputDecoration(
labelText: "用戶名",
hintText: "用戶名或郵箱",
prefixIcon: Icon(Icons.person)),
),
TextField(
//隱藏正在編輯的文本
obscureText: true,
focusNode: focusNode2,
decoration: InputDecoration(
labelText: "密碼",
hintText: "您的登錄密碼",
prefixIcon: Icon(Icons.lock)),
),
custom(),
],
);
}
}
獲取輸入內(nèi)容
獲取輸入內(nèi)容有兩種方式:
- 定義兩個(gè)變量裙顽,用于保存用戶名和密碼付燥,然后在
onChange
觸發(fā)時(shí),各自保存一下輸入內(nèi)容愈犹。 - 通過
controller
直接獲取键科。
onChange獲得輸入內(nèi)容:
TextField(
onChanged: (s) => debugPrint("ssss:$s"),
)
controller獲取:
定義一個(gè)controller:
//定義一個(gè)controller
TextEditingController _unameController=new TextEditingController();
然后設(shè)置輸入框controller:
TextField(
controller: _unameController, //設(shè)置controller
...
)
通過controller獲取輸入框內(nèi)容
debugPrint(_unameController.text)
TextFormField
TextFormField
比TextField
多了一些屬性,其中 validator用于設(shè)置驗(yàn)證回調(diào)漩怎。在單獨(dú)使用時(shí)與TextField
沒有太大的區(qū)別勋颖。當(dāng)結(jié)合From
,利用From
可以對輸入框進(jìn)行分組勋锤,然后進(jìn)行一些統(tǒng)一操作(驗(yàn)證)
class _TextFocusState extends State<TextFocusWidget> {
//全局key
GlobalKey<FormState> _key = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
//類似 id
key: _key,
child: Column(
children: <Widget>[
TextFormField(
autofocus: true,
decoration: InputDecoration(
labelText: "用戶名",
hintText: "用戶名或郵箱",
icon: Icon(Icons.person)),
// 校驗(yàn)用戶名
validator: (v) {
return v.trim().length > 0 ? null : "用戶名不能為空";
}),
TextFormField(
decoration: InputDecoration(
labelText: "密碼",
hintText: "您的登錄密碼",
icon: Icon(Icons.lock)),
// 校驗(yàn)用戶名
validator: (v) {
return v.trim().length > 0 ? null : "密碼不能為空";
}),
RaisedButton(
onPressed: () {
//Form所有TextFormField成功 返回true
if (_key.currentState.validate()) {
}
},
child: Text("提交"),
)
],
));
}
}