圖文講解表單元件,建立表單元件、校驗表單、複雜表單、複雜校驗規則、動態控制表單
實現一個註冊介面
上一次講了登陸介面&表單校驗
知道了表單和校驗怎麼寫,回顧下
建立表單元件
建立form元件
Form元件函式(準確說叫widget),然後寫一個key,因為我們等下要操作它,然後寫child,裡面就是TextFormField元件。
new Form(
key: _formKey,
child: Column(children: [
TextFormField()
]
)
複製程式碼
填寫校驗規則
TextFormField有validator校驗函式 手機號校驗例子:
new TextFormField(
validator: (value){
RegExp reg = new RegExp(r'^1{1}\d{10}$');
if (!reg.hasMatch(value)) {
return '請輸入正確的手機號碼';
}else{
return null;
}
},
)
複製程式碼
這時候雖然有了validator,但是並沒有輸入的載體,我們需要加一個輸入框
final phone = new TextFormField(
keyboardType: TextInputType.number,
autofocus: false,
controller: _phone_controller,
onSaved: (val)=> this._phone = val,
validator: (value){
RegExp reg = new RegExp(r'^1{1}\d{10}$');
if (!reg.hasMatch(value)) {
return '請輸入正確的手機號碼';
}else{
return null;
}
},
decoration: new InputDecoration(
hintText: '手機號',
contentPadding: new EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: new OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
)
)
);
複製程式碼
這裡的話,我們加了一個keyboardType
,設定成了TextInputType.number
,這樣呼叫的數數字鍵盤。
觸發校驗規則
前面Form裡面有一個key,但是我們沒有宣告,需要宣告下
GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
複製程式碼
然後form.validate()
檢測有沒有按規則填寫,form.save()
是儲存資料,這時候上面TextFormField
裡面的onSaved
就會被呼叫,然後資料就被儲存下來
final form = _formKey.currentState;
if(form.validate()) {
form.save();
// 操作邏輯,剛剛的_phone欄位就可以列印了,就是輸入的手機號
}
複製程式碼
然後我們就畫UI、把上面的表單加上,點選註冊的時候獲取值,美滋滋。
但是這時候,點選傳送驗證碼的時候,我們沒法拿到手機號,因為需要先.save的時候才能拿到值,這時候執行的時候也會觸發校驗規則,但是我只要手機號而已,怎麼整。
傳送驗證碼
控制器
建立一個控制器
TextEditingController mController=TextEditingController();
複製程式碼
TextFormField
裡引入
TextFormField(
...
controller: mController
...
)
複製程式碼
點選傳送驗證碼的時候,獲取手機號
mController.text
複製程式碼
我靠,完美。但是... 好像這時候缺少了校驗,所以上面方案不能用。
巢狀Form
這時候,我們需要寫兩個Form元件,也就是在phone元件外面再加一個Form元件
phone元件
改成
new Form( key: _phone_form_key, child: phone, ),
兩個Form格式如下
new Form(
key: _formKey,
child: Container(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
程式碼
Container(
color: Colors.white,
child: new Form(
key: _phone_form_key,
child: phone,
),
),
程式碼
)
)
)
複製程式碼
這時候傳送驗證碼的表單就搞完了,我們就可以註冊。
註冊完成後返回上一個路由並且傳遞引數
(這時候返回的上一個頁面也就是登入頁)
登入頁拿到手機號後,要在輸入框出現手機號,這樣使用者就不用再次輸入手機號了。(有些app是註冊沒有密碼,然後直接幫他登入,密碼是手機號後四位數,然後彈窗確定是否修改密碼,不修改直接登入成功)
返回上一個路由
返回到上頁面,把手機號傳遞過去
返回上一個路由並且傳遞引數
// 註冊成功 轉跳到登陸頁面
Navigator.pop(context, _phone);
複製程式碼
這時候登入頁就需要接收手機號, 上一頁頁面進入下一個頁面的時候有路由轉跳,返回的時候,第二個引數值回返回回來。
上一個頁面接收資料
登入頁點選註冊的按鈕新增事件
onRigister() async{
final result = await Navigator.pushNamed(context, '/rigister');
_phone_controller.text = result;
}
複製程式碼
表單設定手機號
_phone_controller.text = result;
複製程式碼
這個_phone_controller
就是phone元件的控制器,控制器上面介紹了。
_phone_controller.text
這個即可以獲取值,也可以設定值,很強大。
確認密碼
如果要實現確認密碼,那麼在validator
裡面,呼叫 密碼1和密碼2的控制器,.text就可以拿到值判斷了。
完整程式碼
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cjyy/utils/http.dart';
import 'package:cjyy/utils/toast.dart';
import 'dart:convert';
import '../login/index.dart';
class registerPage extends StatefulWidget {
@override
_registerPageState createState() => new _registerPageState();
}
class _registerPageState extends State<registerPage> {
bool isButtonEnable=true; //按鈕狀態 是否可點選
String buttonText='傳送驗證碼'; //初始文字
int count=60; //初始倒數計時時間
Timer timer; //倒數計時的計時器
TextEditingController mController=TextEditingController();
// 提交
GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
GlobalKey<FormState> _phone_form_key = new GlobalKey<FormState>();
String _phone = '';
String _pw = '';
String _code = '';
// 密碼顯示、隱藏
bool _isObscure = true;
Color _eyeColor;
void _buttonClickListen() async{
if(isButtonEnable){ //當按鈕可點選時
final form = _phone_form_key.currentState;
if(form.validate()){
form.save();
// if(!new RegExp(r"1{1}\d{10}").hasMatch(_phone)){
// Toast.show("手機號格式錯誤哦", context);
// print(_phone);
// return null;
// }
String response_content = await HTTP('get:sendSms', {
"phone":_phone,
});
Map<String, Object> response_map = jsonDecode(response_content);
if(response_map['code'] != 200){
print('傳送驗證碼失敗');
Toast.show(response_map['msg'], context);
return null;
}
// 驗證碼傳送成功
isButtonEnable=false; //按鈕狀態標記
_initTimer();
setState(() {
isButtonEnable=false; //按鈕狀態標記
_initTimer();
});
return null; //返回null按鈕禁止點選
}
}
return null;
}
@override
void initState() {
super.initState();
}
void _initTimer(){
timer = new Timer.periodic(Duration(seconds: 1), (Timer timer) {
count--;
setState(() {
if(count==0){
timer.cancel(); //倒數計時結束取消定時器
isButtonEnable=true; //按鈕可點選
count=60; //重置時間
buttonText='傳送驗證碼'; //重置按鈕文字
}else{
buttonText='重新傳送($count)'; //更新文字內容
}
});
});
}
@override
void dispose() {
timer?.cancel(); //銷燬計時器
timer=null;
super.dispose();
}
@override
Widget build(BuildContext context) {
final phone = new TextFormField(
keyboardType: TextInputType.number,
autofocus: false,
initialValue: '',
onSaved: (val)=> this._phone = val,
// onEditingComplete: () => {},
validator: (value){
RegExp reg = new RegExp(r'^1{1}\d{10}$');
if (!reg.hasMatch(value)) {
return '請輸入正確的手機號碼';
}else{
return null;
}
},
decoration: new InputDecoration(
hintText: '手機號',
contentPadding: new EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: new OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
)
)
);
final password = new TextFormField(
autofocus: false,
initialValue: '',
onSaved: (val)=> this._pw = val,
obscureText: _isObscure,
validator: (value){
if(value.length < 6 || value.length > 16){
return '密碼在6-16位數之間哦';
}else{
return null;
}
},
decoration: new InputDecoration(
hintText: '密碼',
contentPadding: new EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: new OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
suffixIcon: IconButton(
icon: Icon(
Icons.remove_red_eye,
color: _eyeColor,
),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
_eyeColor = _isObscure
? Colors.grey
: Theme.of(context).iconTheme.color;
});
}),
),
);
onsubmit() async{
final form = _formKey.currentState;
final phoneform = _phone_form_key.currentState;
// // 測試 註冊成功 轉跳到登陸頁面
// Navigator.pop(context, 'test');
if(phoneform.validate() && form.validate()) {
form.save();
phoneform.save();
print('註冊中');
print(_phone);
print(_pw);
String response_content = await HTTP('get:phone_rigist', {
"phone":_phone,
"pass": _pw,
"code": _code,
});
Map<String, Object> response_map = jsonDecode(response_content);
if(response_map['code'] != 200){
print('註冊異常');
Toast.show(response_map['msg'], context);
return;
}
// 註冊成功 轉跳到登陸頁面
Navigator.pop(context, _phone);
}
}
return Container(
child: Scaffold(
appBar: AppBar(
brightness: Brightness.light,
title: Text('註冊'),
),
body: new Form(
key: _formKey,
child: Container(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
Container(height: 200,),
Container(
color: Colors.white,
// padding: EdgeInsets.only(left: 10,right: 10),
child: new Form(
key: _phone_form_key,
child: phone,
),
),
Container(
// height: 70,
color: Colors.white,
padding: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.ideographic,
children: <Widget>[
// Text('驗證碼',style: TextStyle(fontSize: 13,color: Color(0xff333333)),),
Expanded(
child: TextFormField(
keyboardType: TextInputType.number,
// maxLines: 6,
// maxLength: 1,
onSaved: (value) => this._code = value,
validator: (value){
if(value.length != 4){
return '驗證碼至少4位數';
}
return null;
},
controller: mController,
textAlign: TextAlign.left,
// inputFormatters: [WhitelistingTextInputFormatter.digitsOnly,LengthLimitingTextInputFormatter(6)],
decoration: InputDecoration(
hintText: ('填寫驗證碼'),
contentPadding: new EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
// contentPadding: EdgeInsets.only(top: -5,bottom: 0),
hintStyle: TextStyle(
color: Color(0xff999999),
fontSize: 13,
),
alignLabelWithHint: true,
// border: OutlineInputBorder(borderSide: BorderSide.none),
border: new OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0)
),
),
)
),
Container(
margin: EdgeInsets.only(left: 20),
width: 120,
child: FlatButton(
disabledColor: Colors.grey.withOpacity(0.1), //按鈕禁用時的顏色
disabledTextColor: Colors.white, //按鈕禁用時的文字顏色
textColor:isButtonEnable?Colors.white:Colors.black.withOpacity(0.2), //文字顏色
color: isButtonEnable?Color(0xff44c5fe):Colors.grey.withOpacity(0.1), //按鈕的顏色
splashColor: isButtonEnable?Colors.white.withOpacity(0.1):Colors.transparent,
shape: StadiumBorder(side: BorderSide.none),
onPressed: (){ setState(() {
_buttonClickListen();
});},
// child: Text('重新傳送 (${secondSy})'),
child: Text('$buttonText',style: TextStyle(fontSize: 13,),),
),
),
],
),
),
password,
Container(
width: double.infinity,
height: 45,
margin: EdgeInsets.only(top: 50),
child: RaisedButton(
onPressed: () {
onsubmit();
},
shape: StadiumBorder(side: BorderSide.none),
color: Color(0xff44c5fe),
child: Text(
'註冊',
style: TextStyle(color: Colors.white,fontSize: 15),
),
),
),
],
)
)
)
),
);
}
}
複製程式碼