Flutter學(xué)習(xí)指南(8):重要控件TextField和處理文本的Form

文本框TextField

屬性列表

 TextEditingController controller, 
  FocusNode focusNode,
  InputDecoration decoration = const InputDecoration(),
  TextInputType keyboardType,
  TextInputAction textInputAction,
  TextStyle style,
  TextAlign textAlign = TextAlign.start,
  bool autofocus = false,
  bool obscureText = false,
  int maxLines = 1,
  int maxLength,
  bool maxLengthEnforced = true,
  ValueChanged<String> onChanged,
  VoidCallback onEditingComplete,
  ValueChanged<String> onSubmitted,
  List<TextInputFormatter> inputFormatters,
  bool enabled,
  this.cursorWidth = 2.0,
  this.cursorRadius,
  this.cursorColor,

屬性講解

`controller` :編輯框的控制器登失,通過(guò)它可以設(shè)置/獲取編輯框的內(nèi)容、選擇編輯內(nèi)容挖炬、監(jiān)聽(tīng)編輯文本改變事件揽浙。大多數(shù)情況下我們都需要顯式提供一個(gè)controller來(lái)與文本框交互。如果沒(méi)有提供controller意敛,則TextField內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)馅巷。

`focusNode`:用于控制TextField是否占有當(dāng)前鍵盤(pán)的輸入焦點(diǎn)。它是我們和鍵盤(pán)交互的一個(gè)handle草姻。

`InputDecoration`:用于控制TextField的外觀顯示钓猬,如提示文本、背景顏色撩独、邊框等敞曹。

`keyboardType`:用于設(shè)置該輸入框默認(rèn)的鍵盤(pán)輸入類(lèi)型,取值如下:
| TextInputType枚舉值 | 含義 | | ----- | -------- | | text | 文本輸入鍵盤(pán) | | 
multiline | 多行文本综膀,需和maxLines配合使用(設(shè)為null或大于1) | | number 
| 數(shù)字澳迫;會(huì)彈出數(shù)字鍵盤(pán) | | phone | 優(yōu)化后的電話號(hào)碼輸入鍵盤(pán);會(huì)彈出數(shù)
字鍵盤(pán)并顯示"* #" | | datetime | 優(yōu)化后的日期輸入鍵盤(pán)剧劝;Android上會(huì)顯
示“: -” | | emailAddress | 優(yōu)化后的電子郵件地址橄登;會(huì)顯示“@ .” | | url | 優(yōu)化
后的url輸入鍵盤(pán); 會(huì)顯示“/ .” |

`textInputAction` :鍵盤(pán)動(dòng)作按鈕圖標(biāo)(即回車(chē)鍵位圖標(biāo))担平,它是一個(gè)枚舉值示绊,有多個(gè)可選值,全部的取值列表讀者可以查看API文檔暂论,下面是當(dāng)值為T(mén)extInputAction.search時(shí),鍵盤(pán)右下角有搜索拌禾,前往等設(shè)置取胎。
`style`:正在編輯的文本樣式。
`textAlign`: 輸入框內(nèi)編輯文本在水平方向的對(duì)齊方式湃窍。
`autofocus`: 是否自動(dòng)獲取焦點(diǎn)闻蛀。
`obscureText`:是否隱藏正在編輯的文本,如用于輸入密碼的場(chǎng)景等您市,文本內(nèi)容會(huì)用“?”替換觉痛。
`maxLines`:輸入框的最大行數(shù),默認(rèn)為1茵休;如果為null薪棒,則無(wú)行數(shù)限制手蝎。
`maxLength`和`maxLengthEnforced` :maxLength代表輸入框文本的最大長(zhǎng)度,設(shè)置后輸入框右下角會(huì)顯示輸入的文本計(jì)數(shù)俐芯。maxLengthEnforced決定當(dāng)輸入文本長(zhǎng)度超過(guò)maxLength時(shí)是否阻止輸入棵介,為true時(shí)會(huì)阻止輸入,為false時(shí)不會(huì)阻止輸入但輸入框會(huì)變紅吧史。
`onChange`:輸入框內(nèi)容改變時(shí)的回調(diào)函數(shù)邮辽;注:內(nèi)容改變事件也可以通過(guò)controller來(lái)監(jiān)聽(tīng)。
`onEditingComplete`和`onSubmitted`:這兩個(gè)回調(diào)都是在輸入框輸入完成時(shí)觸發(fā)贸营,比如按了鍵盤(pán)的完成鍵(對(duì)號(hào)圖標(biāo))或搜索鍵(??圖標(biāo))吨述。不同的是兩個(gè)回調(diào)簽名不同,onSubmitted回調(diào)是ValueChanged<String>類(lèi)型钞脂,它接收當(dāng)前輸入內(nèi)容做為參數(shù)锐极,而onEditingComplete不接收參數(shù)。
`inputFormatters`:用于指定輸入格式芳肌;當(dāng)用戶輸入內(nèi)容改變時(shí)灵再,會(huì)根據(jù)指定的格式來(lái)校驗(yàn)。
`enable`:如果為false亿笤,則輸入框會(huì)被禁用翎迁,禁用狀態(tài)不接收輸入和事件,同時(shí)顯示禁用態(tài)樣式(在其decoration中定義)净薛。
`cursorWidth`汪榔、`cursorRadius`和`cursorColor`:這三個(gè)屬性是用于自定義輸入框光標(biāo)寬度、圓角和顏色的肃拜。

外觀設(shè)置

TextField(
      controller: controller,
      decoration: InputDecoration(
                labelText: "密碼", ## labelText設(shè)置標(biāo)簽文字痴腌,這個(gè)標(biāo)簽在沒(méi)有輸入的時(shí)候是占滿輸入框的,當(dāng)輸入聚焦以后燃领,就會(huì)縮小到輸入框左上角:
                hintText: "您的登錄密碼",    ##placeholder
                prefixIcon: Icon(Icons.lock)   ## 前面的icon
                fillColor: Colors.blue.shade100,  ##填充顏色
                filled: true, ## 充滿
                errorText: 'error'  ## 錯(cuò)誤提示
                helperText: 'help',  ##提示文本
                 suffixText: 'airport',   ##后綴
                 // 邊框設(shè)置
               // border: InputBorder.none //隱藏下劃線
                 contentPadding: EdgeInsets.all(10.0),
                border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(15.0),
                borderSide: BorderSide(color: Colors.red, width: 3.0, style: 
                           BorderStyle.solid)//沒(méi)什么卵效果
                                      )),
                   ),
      maxLength: 30,//最大長(zhǎng)度士聪,設(shè)置此項(xiàng)會(huì)讓TextField右下角有一個(gè)輸入數(shù)量的統(tǒng)計(jì)字符串
      maxLines: 1,//最大行數(shù)
      autocorrect: true,//是否自動(dòng)更正
      autofocus: true,//是否自動(dòng)對(duì)焦
      obscureText: true,//是否是密碼
      textAlign: TextAlign.center,//文本對(duì)齊方式
      style: TextStyle(fontSize: 30.0, color: Colors.blue),//輸入文本的樣式
      inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],//允許的輸入格式
      enabled: true,//不禁用
    );

監(jiān)聽(tīng)字符變化,設(shè)置默認(rèn)字符

要監(jiān)聽(tīng)就要有監(jiān)聽(tīng)控制器 TextEditingController()

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class TextFieldPage extends StatelessWidget {
  Widget buildTextField(TextEditingController controller) {
    return TextField(
      controller: controller,
      maxLength: 30,//最大長(zhǎng)度猛蔽,設(shè)置此項(xiàng)會(huì)讓TextField右下角有一個(gè)輸入數(shù)量的統(tǒng)計(jì)字符串
      maxLines: 1,//最大行數(shù)
      autocorrect: true,//是否自動(dòng)更正
      autofocus: true,//是否自動(dòng)對(duì)焦
      obscureText: true,//是否是密碼
      textAlign: TextAlign.center,//文本對(duì)齊方式
      style: TextStyle(fontSize: 30.0, color: Colors.blue),//輸入文本的樣式
      inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],//允許的輸入格式
      onChanged: (text) {//內(nèi)容改變的回調(diào)
        print('change $text');
      },
      onSubmitted: (text) {//內(nèi)容提交(按回車(chē))的回調(diào)
        print('submit $text');
      },
      enabled: true,//是否禁用
    );
  }

  @override
  Widget build(BuildContext context) {
    final controller = TextEditingController();
    controller.text="hello world!";   ## 設(shè)置默認(rèn)值
    controller.selection=TextSelection(
         baseOffset: 2,  ###并從第三個(gè)字符開(kāi)始選中后面的字符
         extentOffset: controller.text.length
     );
    controller.addListener(() {   ## 監(jiān)聽(tīng)
      print('input ${controller.text}');
    });
    return Scaffold(
      appBar: AppBar(
        title: Text('TextField'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: buildTextField(controller),
      ),
    );
  }
}

鍵盤(pán)相關(guān)
FocusNode和FocusScopeNode兩個(gè)屬性控制

class FocusTestRoute extends StatefulWidget {
  @override
  _FocusTestRouteState createState() => new _FocusTestRouteState();
}

class _FocusTestRouteState extends State<FocusTestRoute> {
  FocusNode focusNode1 = new FocusNode();   ## 初始化對(duì)象
  FocusNode focusNode2 = new FocusNode();    ## 初始化對(duì)象
  FocusScopeNode focusScopeNode;
// 監(jiān)聽(tīng)焦點(diǎn)變化    
focusNode.addListener((){
   print(focusNode.hasFocus);
});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16.0),
      child: Column(
        children: <Widget>[
          TextField(
            autofocus: true, 
            focusNode: focusNode1,                   //關(guān)聯(lián)focusNode1
            decoration: InputDecoration(
                labelText: "input1"
            ),
          ),
          TextField(
            focusNode: focusNode2,                //關(guān)聯(lián)focusNode2
            decoration: InputDecoration(
                labelText: "input2"
            ),
          ),
          Builder(builder: (ctx) {
            return Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("移動(dòng)焦點(diǎn)"),
                  onPressed: () {
                    //將焦點(diǎn)從第一個(gè)TextField移到第二個(gè)TextField
                    // 這是一種寫(xiě)法 FocusScope.of(context).requestFocus(focusNode2);
                    // 這是第二種寫(xiě)法
                    if(null == focusScopeNode){
                         focusScopeNode = FocusScope.of(context);   ##初始化焦點(diǎn)第一個(gè)剥悟,從全文按順序查找
                    }
                    focusScopeNode.requestFocus(focusNode2);      ##移動(dòng)焦點(diǎn)到第二個(gè)
                  },
                ),
                RaisedButton(
                  child: Text("隱藏鍵盤(pán)"),
                  onPressed: () {
                    // 當(dāng)所有編輯框都失去焦點(diǎn)時(shí)鍵盤(pán)就會(huì)收起  
                    focusNode1.unfocus();    ### 取消焦點(diǎn)操作
                    focusNode2.unfocus();    ### 取消焦點(diǎn)操作
                  },
                ),
              ],
            );
          },
          ),
        ],
      ),
    );
  }

}

From

對(duì)一組文本進(jìn)行相同的校驗(yàn)
Form 的子空間必須是FormField(和textfild基本一樣)
Form屬性

Form({
  @required Widget child,
  bool autovalidate = false,  ##是否自動(dòng)校驗(yàn)輸入內(nèi)容
  WillPopCallback onWillPop,   ##攔截返回按鈕
  VoidCallback onChanged,   ##Form的任意一個(gè)子FormField內(nèi)容發(fā)生變化時(shí)會(huì)觸發(fā)此回調(diào)
})

FormField

const FormField({
  ...
  FormFieldSetter<T> onSaved, //保存回調(diào)
  FormFieldValidator<T>  validator, //驗(yàn)證回調(diào)
  T initialValue, //初始值
  bool autovalidate = false, //是否自動(dòng)校驗(yàn)。
})

FormState

FormState為Form的State類(lèi)曼库,可以通過(guò)Form.of()或GlobalKey獲得区岗。我們可以通過(guò)它來(lái)對(duì)Form的子孫FormField進(jìn)行統(tǒng)一操作。我們看看其常用的三個(gè)方法:
FormState.validate():調(diào)用此方法后毁枯,會(huì)調(diào)用Form子孫FormField的validate回調(diào)慈缔,如果有一個(gè)校驗(yàn)失敗,則返回false种玛,所有校驗(yàn)失敗項(xiàng)都會(huì)返回用戶返回的錯(cuò)誤提示藐鹤。
FormState.save():調(diào)用此方法后瓤檐,會(huì)調(diào)用Form子孫FormField的save回調(diào),用于保存表單內(nèi)容
FormState.reset():調(diào)用此方法后教藻,會(huì)將子孫FormField的內(nèi)容清空距帅。

例子

class FormTestRoute extends StatefulWidget {
  @override
  _FormTestRouteState createState() => new _FormTestRouteState();
}

class _FormTestRouteState extends State<FormTestRoute> {
  TextEditingController _unameController = new TextEditingController();
  TextEditingController _pwdController = new TextEditingController();
  GlobalKey _formKey= new GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title:Text("Form Test"),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
        child: Form(
          key: _formKey, //設(shè)置globalKey,用于后面獲取FormState
          autovalidate: true, //開(kāi)啟自動(dòng)校驗(yàn)
          child: Column(
            children: <Widget>[
              TextFormField(
                  autofocus: true,
                  controller: _unameController,
                  decoration: InputDecoration(
                      labelText: "用戶名",
                      hintText: "用戶名或郵箱",
                      icon: Icon(Icons.person)
                  ),
                  // 校驗(yàn)用戶名
                  validator: (v) {
                    return v
                        .trim()
                        .length > 0 ? null : "用戶名不能為空";
                  }

              ),
              TextFormField(
                  controller: _pwdController,
                  decoration: InputDecoration(
                      labelText: "密碼",
                      hintText: "您的登錄密碼",
                      icon: Icon(Icons.lock)
                  ),
                  obscureText: true,
                  //校驗(yàn)密碼
                  validator: (v) {
                    return v
                        .trim()
                        .length > 5 ? null : "密碼不能少于6位";
                  }
              ),
              // 登錄按鈕
              Padding(
                padding: const EdgeInsets.only(top: 28.0),
                child: Row(
                  children: <Widget>[
                    Expanded(
                      child: RaisedButton(
                        padding: EdgeInsets.all(15.0),
                        child: Text("登錄"),
                        color: Theme
                            .of(context)
                            .primaryColor,
                        textColor: Colors.white,
                        onPressed: () {
                          //在這里不能通過(guò)此方式獲取FormState括堤,context不對(duì)
                          //print(Form.of(context));

                          // 通過(guò)_formKey.currentState 獲取FormState后碌秸,
                          // 調(diào)用validate()方法校驗(yàn)用戶名密碼是否合法,校驗(yàn)
                          // 通過(guò)后再提交數(shù)據(jù)悄窃。 
                          if((_formKey.currentState as FormState).validate()){
                            //驗(yàn)證通過(guò)提交數(shù)據(jù)
                          }
                        },
                      ),
                    ),
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讥电,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子轧抗,更是在濱河造成了極大的恐慌恩敌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件横媚,死亡現(xiàn)場(chǎng)離奇詭異纠炮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)灯蝴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)恢口,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人穷躁,你說(shuō)我怎么就攤上這事耕肩。” “怎么了问潭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵猿诸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我狡忙,道長(zhǎng)梳虽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任去枷,我火速辦了婚禮怖辆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘删顶。我一直安慰自己,他們只是感情好淑廊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布逗余。 她就那樣靜靜地躺著,像睡著了一般季惩。 火紅的嫁衣襯著肌膚如雪录粱。 梳的紋絲不亂的頭發(fā)上腻格,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音啥繁,去河邊找鬼菜职。 笑死,一個(gè)胖子當(dāng)著我的面吹牛旗闽,可吹牛的內(nèi)容都是我干的酬核。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼适室,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嫡意!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起捣辆,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蔬螟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后汽畴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體旧巾,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年忍些,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鲁猩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坐昙,死狀恐怖绳匀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炸客,我是刑警寧澤疾棵,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站痹仙,受9級(jí)特大地震影響是尔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜开仰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一拟枚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧众弓,春花似錦恩溅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至滨达,卻和暖如春奶稠,著一層夾襖步出監(jiān)牢的瞬間俯艰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工锌订, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竹握,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓辆飘,卻偏偏與公主長(zhǎng)得像啦辐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子劈猪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容