<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 輸入框及表單 Material widget庫中提供了豐富的輸入框及表單Widget。下面我們分別介紹一下。 ### TextField TextField用于文本輸入,它提供了很多屬性,我們先簡單介紹一下主要屬性的作用,然后通過幾個示例來演示一下關鍵屬性的用法。 ``` const 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:編輯框的控制器,通過它可以設置/獲取編輯框的內容、選擇編輯內容、監聽編輯文本改變事件。大多數情況下我們都需要顯式提供一個controller來與文本框交互。如果沒有提供controller,則TextField內部會自動創建一個。 - focusNode:用于控制TextField是否占有當前鍵盤的輸入焦點。它是我們和鍵盤交互的一個handle。 - InputDecoration:用于控制TextField的外觀顯示,如提示文本、背景顏色、邊框等。 - keyboardType:用于設置該輸入框默認的鍵盤輸入類型,取值如下: | TextInputType枚舉值 | 含義 | | ------------------- | --------------------------------------------------- | | text | 文本輸入鍵盤 | | multiline | 多行文本,需和maxLines配合使用(設為null或大于1) | | number | 數字;會彈出數字鍵盤 | | phone | 優化后的電話號碼輸入鍵盤;會彈出數字鍵盤并顯示"\* #" | | datetime | 優化后的日期輸入鍵盤;Android上會顯示“: -” | | emailAddress | 優化后的電子郵件地址;會顯示“@ .” | | url | 優化后的url輸入鍵盤; 會顯示“/ .” | - textInputAction:鍵盤動作按鈕圖標(即回車鍵位圖標),它是一個枚舉值,有多個可選值,全部的取值列表讀者可以查看API文檔,下面是當值為`TextInputAction.search`時,原生Android系統下鍵盤樣式: ![](https://box.kancloud.cn/68d0356157a4cd166bb9546e9d85424d_380x274.png) #### - style:正在編輯的文本樣式。 - textAlign: 輸入框內編輯文本在水平方向的對齊方式。 - autofocus: 是否自動獲取焦點。 - obscureText:是否隱藏正在編輯的文本,如用于輸入密碼的場景等,文本內容會用“?”替換。 - maxLines:輸入框的最大行數,默認為1;如果為`null`,則無行數限制。 - maxLength和maxLengthEnforced :maxLength代表輸入框文本的最大長度,設置后輸入框右下角會顯示輸入的文本計數。maxLengthEnforced決定當輸入文本長度超過maxLength時是否阻止輸入,為true時會阻止輸入,為false時不會阻止輸入但輸入框會變紅。 - onChange:輸入框內容改變時的回調函數;注:內容改變事件也可以通過controller來監聽。 - onEditingComplete和onSubmitted:這兩個回調都是在輸入框輸入完成時觸發,比如按了鍵盤的完成鍵(對號圖標)或搜索鍵(??圖標)。不同的是兩個回調簽名不同,onSubmitted回調是`ValueChanged<String>`類型,它接收當前輸入內容做為參數,而onEditingComplete不接收參數。 - inputFormatters:用于指定輸入格式;當用戶輸入內容改變時,會根據指定的格式來校驗。 - enable:如果為`false`,則輸入框會被禁用,禁用狀態不接收輸入和事件,同時顯示禁用態樣式(在其decoration中定義)。 - cursorWidth、cursorRadius和cursorColor:這三個屬性是用于自定義輸入框光標寬度、圓角和顏色的。 #### 示例:登錄輸入框 ##### 布局 ``` Column( children: <Widget>[ TextField( autofocus: true, decoration: InputDecoration( labelText: "用戶名", hintText: "用戶名或郵箱", prefixIcon: Icon(Icons.person) ), ), TextField( decoration: InputDecoration( labelText: "密碼", hintText: "您的登錄密碼", prefixIcon: Icon(Icons.lock) ), obscureText: true, ), ], ); ``` ![](https://box.kancloud.cn/18d092337e5483f865d9849e1f8471ec_360x158.png) ##### 獲取輸入內容 獲取輸入內容有兩種方式: 1. 定義兩個變量,用于保存用戶名和密碼,然后在onChange觸發時,各自保存一下輸入內容。 2. 通過controller直接獲取。 第一種方式比較簡單,不在舉例,我們來重點看一下第二種方式,我們以用戶名輸入框舉例: 定義一個controller: ``` //定義一個controller TextEditingController _unameController=new TextEditingController(); ``` 然后設置輸入框controller: ``` TextField( autofocus: true, controller: _unameController, //設置controller ... ) ``` 通過controller獲取輸入框內容 ``` print(_unameController.text) ``` ##### 監聽文本變化 監聽文本變化也有兩種方式: 1. 設置onChange回調,如: ``` TextField( autofocus: true, onChanged: (v) { print("onChange: $v"); } ) ``` 2. 通過controller監聽,如: ``` @override void initState() { //監聽輸入改變 _unameController.addListener((){ print(_unameController.text); }); } ``` 兩種方式相比,onChanged是專門用于監聽文本變化,而controller的功能卻多一些,除了能監聽文本變化外,它還可以設置默認值、選擇文本,下面我們看一個例子: 創建一個controller: ``` TextEditingController _selectionController = new TextEditingController(); ``` 設置默認值,并從第三個字符開始選中后面的字符 ``` _selectionController.text="hello world!"; _selectionController.selection=TextSelection( baseOffset: 2, extentOffset: _selectionController.text.length ); ``` 設置controller: ``` TextField( controller: _selectionController, ) ``` 運行效果如下: ![](https://box.kancloud.cn/b3e67651ee0500af2d927906e15b5a9c_360x43.png) ##### 控制焦點 焦點可以通過FocusNode和FocusScopeNode來控制,默認情況下,焦點由FocusScope來管理,它代表焦點控制范圍,可以在這個范圍內可以通過FocusScopeNode在輸入框之間移動焦點、設置默認焦點等。我們可以通過`FocusScope.of(context)` 來獲取widget樹中默認的FocusScopeNode。下面看一個示例,在此示例中創建兩個TextField,第一個自動獲取焦點,然后創建兩個按鈕: - 點擊第一個按鈕可以將焦點從第一個TextField挪到第二個TextField。 - 點擊第二個按鈕可以關閉鍵盤。 界面如下: ![](https://box.kancloud.cn/6ff2b58cfb8e52f15ab72734d86eed71_360x571.png) 代碼如下: ``` class FocusTestRoute extends StatefulWidget { @override _FocusTestRouteState createState() => new _FocusTestRouteState(); } class _FocusTestRouteState extends State<FocusTestRoute> { FocusNode focusNode1 = new FocusNode(); FocusNode focusNode2 = new FocusNode(); FocusScopeNode focusScopeNode; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(16.0), child: Column( children: <Widget>[ TextField( autofocus: true, focusNode: focusNode1,//關聯focusNode1 decoration: InputDecoration( labelText: "input1" ), ), TextField( focusNode: focusNode2,//關聯focusNode2 decoration: InputDecoration( labelText: "input2" ), ), Builder(builder: (ctx) { return Column( children: <Widget>[ RaisedButton( child: Text("移動焦點"), onPressed: () { //將焦點從第一個TextField移到第二個TextField // 這是一種寫法 FocusScope.of(context).requestFocus(focusNode2); // 這是第二種寫法 if(null == focusScopeNode){ focusScopeNode = FocusScope.of(context); } focusScopeNode.requestFocus(focusNode2); }, ), RaisedButton( child: Text("隱藏鍵盤"), onPressed: () { // 當所有編輯框都失去焦點時鍵盤就會收起 focusNode1.unfocus(); focusNode2.unfocus(); }, ), ], ); }, ), ], ), ); } } ``` FocusNode和FocusScopeNode還有一些其它的方法,詳情可以查看API文檔。 ##### 監聽焦點狀態改變事件 FocusNode繼承自ChangeNotifier,通過FocusNode可以監聽焦點的改變事件,如: ``` ... // 創建 focusNode FocusNode focusNode = new FocusNode(); ... // focusNode綁定輸入框 TextField(focusNode: focusNode); ... // 監聽焦點變化 focusNode.addListener((){ print(focusNode.hasFocus); }); ``` 獲得焦點時`focusNode.hasFocus`值為`true`,失去焦點時為`false`。 ##### 自定義樣式 雖然我們可以通過decoration屬性來定義輸入框樣式,但是有一些樣式如下劃線默認顏色及寬度都是不能直接自定義的,下面的代碼**沒有效果**: ``` TextField( ... decoration: InputDecoration( border: UnderlineInputBorder( //下面代碼沒有效果 borderSide: BorderSide( color: Colors.red, width: 5.0 )), prefixIcon: Icon(Icons.person) ), ), ``` 之所以如此,是由于TextField在繪制下劃線時使用的顏色是主題色里面的`hintColor`,但提示文本顏色也是用的`hintColor`, 如果我們直接修改`hintColor`,那么下劃線和提示文本的顏色都會變。值得高興的是decoration中可以設置`hintStyle`,它可以覆蓋`hintColor`,并且主題中可以通過`inputDecorationTheme`來設置輸入框默認的decoration。所以我們可以通過主題來自定義,代碼如下: ``` Theme( data: Theme.of(context).copyWith( hintColor: Colors.grey[200], //定義下劃線顏色 inputDecorationTheme: InputDecorationTheme( labelStyle: TextStyle(color: Colors.grey),//定義label字體樣式 hintStyle: TextStyle(color: Colors.grey, fontSize: 14.0)//定義提示文本樣式 ) ), child: Column( children: <Widget>[ TextField( decoration: InputDecoration( labelText: "用戶名", hintText: "用戶名或郵箱", prefixIcon: Icon(Icons.person) ), ), TextField( decoration: InputDecoration( prefixIcon: Icon(Icons.lock), labelText: "密碼", hintText: "您的登錄密碼", hintStyle: TextStyle(color: Colors.grey, fontSize: 13.0) ), obscureText: true, ) ], ) ) ``` 運行效果如下: ![](https://box.kancloud.cn/b6b9c9b2fa6f531585aef6a69448bd3c_360x167.png) 我們成功的自定義了下劃線顏色和提問文字樣式,細心的讀者可能已經發現,通過這種方式自定義后,輸入框在獲取焦點時,labelText不會高亮顯示了,正如上圖中的"用戶名"本應該顯示藍色,但現在卻顯示為灰色,并且我們還是無法定義下劃線寬度。另一種靈活的方式是直接隱藏掉TextField本身的下劃線,然后通過Container去嵌套定義樣式,如: ``` Container( child: TextField( keyboardType: TextInputType.emailAddress, decoration: InputDecoration( labelText: "Email", hintText: "電子郵件地址", prefixIcon: Icon(Icons.email), border: InputBorder.none //隱藏下劃線 ) ), decoration: BoxDecoration( // 下滑線淺灰色,寬度1像素 border: Border(bottom: BorderSide(color: Colors.grey[200], width: 1.0)) ), ) ``` 運行效果: ![](https://box.kancloud.cn/b0cab589e4ab7320dd6c6a541ea5b855_360x62.png) 通過這種widget組合的方式,也可以定義背景圓角等。一般來說,優先通過decoration來自定義樣式,如果decoration實現不了,再用widget組合的方式。 > 思考題:在這個示例中,下劃線顏色是固定的,所以獲得焦點后顏色仍然為灰色,如何實現點擊后下滑線也變色呢? ### 表單Form 實際業務中,在正式向服務器提交數據前,都會對各個輸入框數據進行合法性校驗,但是對每一個TextField都分別進行校驗將會是一件很麻煩的事。還有,如果用戶想清除一組TextField的內容,除了一個一個清除有沒有什么更好的辦法呢?為此,Flutter提供了一個Form widget,它可以對輸入框進行分組,然后進行一些統一操作,如輸入內容校驗、輸入框重置以及輸入內容保存。 #### Form Form繼承自StatefulWidget對象,它對應的狀態類為FormState。我們先看看Form類的定義: ``` Form({ @required Widget child, bool autovalidate = false, WillPopCallback onWillPop, VoidCallback onChanged, }) ``` - autovalidate:是否自動校驗輸入內容;當為`true`時,每一個子FormField內容發生變化時都會自動校驗合法性,并直接顯示錯誤信息。否則,需要通過調用`FormState.validate()`來手動校驗。 - onWillPop:決定Form所在的路由是否可以直接返回(如點擊返回按鈕),該回調返回一個`Future`對象,如果Future的最終結果是false,則當前路由不會返回;如果為`true`,則會返回到上一個路由。此屬性通常用于攔截返回按鈕。 - onChanged:Form的任意一個子FormField內容發生變化時會觸發此回調。 #### FormField Form的子孫元素必須是FormField類型,FormField是一個抽象類,定義幾個屬性,FormState內部通過它們來完成操作,FormField部分定義如下: ``` const FormField({ ... FormFieldSetter<T> onSaved, //保存回調 FormFieldValidator<T> validator, //驗證回調 T initialValue, //初始值 bool autovalidate = false, //是否自動校驗。 }) ``` 為了方便使用,Flutter提供了一個TextFormField widget,它繼承自FormField類,也是TextField的一個包裝類,所以除了FormField定義的屬性之外,它還包括TextField的屬性。 #### FormState FormState為Form的State類,可以通過`Form.of()`或GlobalKey獲得。我們可以通過它來對Form的子孫FormField進行統一操作。我們看看其常用的三個方法: - `FormState.validate()`:調用此方法后,會調用Form子孫FormField的validate回調,如果有一個校驗失敗,則返回false,所有校驗失敗項都會返回用戶返回的錯誤提示。 - `FormState.save()`:調用此方法后,會調用Form子孫FormField的save回調,用于保存表單內容 - `FormState.reset()`:調用此方法后,會將子孫FormField的內容清空。 #### 示例 我們修改一下上面用戶登錄的示例,在提交之前校驗: 1. 用戶名不能為空,如果為空則提示“用戶名不能為空”。 2. 密碼不能小于6位,如果小于6為則提示“密碼不能少于6位”。 完整代碼: ``` 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 PageScaffold( title: "Form Test", body: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), child: Form( key: _formKey, //設置globalKey,用于后面獲取FormState autovalidate: true, //開啟自動校驗 child: Column( children: <Widget>[ TextFormField( autofocus: true, controller: _unameController, decoration: InputDecoration( labelText: "用戶名", hintText: "用戶名或郵箱", icon: Icon(Icons.person) ), // 校驗用戶名 validator: (v) { return v .trim() .length > 0 ? null : "用戶名不能為空"; } ), TextFormField( controller: _pwdController, decoration: InputDecoration( labelText: "密碼", hintText: "您的登錄密碼", icon: Icon(Icons.lock) ), obscureText: true, //校驗密碼 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: () { //在這里不能通過此方式獲取FormState,context不對 //print(Form.of(context)); // 通過_formKey.currentState 獲取FormState后, // 調用validate()方法校驗用戶名密碼是否合法,校驗 // 通過后再提交數據。 if((_formKey.currentState as FormState).validate()){ //驗證通過提交數據 } }, ), ), ], ), ) ], ), ), ), ); } } ``` 運行后: ![](https://box.kancloud.cn/ff4de0360f85afdc873b930bbff8ee72_360x332.png) 注意,登錄按鈕的onPressed方法中不能通過`Form.of(context)`來獲取,原因是,此處的context為FormTestRoute的context,而`Form.of(context)`是根據所指定context向根去查找,而FormState是在FormTestRoute的子樹中,所以不行。正確的做法是通過Builder來構建登錄按鈕,Builder會將widget節點的context作為回調參數: ``` Expanded( // 通過Builder來獲取RaisedButton所在widget樹的真正context(Element) child:Builder(builder: (context){ return RaisedButton( ... onPressed: () { //由于本widget也是Form的子代widget,所以可以通過下面方式獲取FormState if(Form.of(context).validate()){ //驗證通過提交數據 } }, ); }) ) ``` 其實context正是操作Widget所對應的Element的一個接口,由于Widget樹對應的Element都是不同的,所以context也都是不同的,有關context的更多內容會在后面高級部分詳細討論。Flutter中有很多“of(context)”這種方法,在使用時讀者一定要注意context是否正確。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看