flutter - 圖文講解表單元件基本使用 & 註冊實戰

秀嶽lonelyBoy發表於2019-10-28

圖文講解表單元件,建立表單元件、校驗表單、複雜表單、複雜校驗規則、動態控制表單

實現一個註冊介面

flutter - 圖文講解表單元件基本使用 & 註冊實戰

上一次講了登陸介面&表單校驗

知道了表單和校驗怎麼寫,回顧下

建立表單元件

建立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),
                    ),
                  ),
                ),
              ],
            )
          )
        )
      ),
    );
  }
}

複製程式碼

相關連結

TextFormField元件官方文件

相關文章