Flutter學習筆記(13)--表單元件

CurtisWgh發表於2019-08-01

如需轉載,請註明出處:Flutter學習筆記(13)--表單元件

表單元件是個包含表單元素的區域,表單元素允許使用者輸入內容,比如:文字區域,下拉表單,單選框、核取方塊等,常見的應用場景有:登陸、註冊、輸入資訊等。表單裡有兩個重要的元件,一個是Form元件用來做整個表單提交使用的,另一個是TextFormField元件用來做使用者輸入的。

Form元件屬性
屬性 型別 說明
Key Key 元件在整個Widget樹中的key值
autovalidate bool 是否自動提交表單
child Widget 元件child只能有一個元件
onChange VoidCallback 當FormField值改變時的回撥函式

 

 

 

 

 

 

 

TextFormFiled元件屬性
屬性名 型別 說明
autovalidate bool 自動驗證值
initalValue T 表單欄位初始值,比如:輸入收穫地址時,預設回填本的地址資訊
onSaved FormFieldSetter<T> 當Form表單呼叫儲存方法Save時回撥的函式
validator FormFieldValidator<T> Form表單驗證器

 

 

 

 

 

 

 

 

 

對於輸入框我們最關心的時輸入內容是否合法,比如郵箱地址是否正確,電話號碼是否是數字等等,等使用者輸入完成後,我們需要知道輸入框輸入的內容。那麼我們要如何才能獲取到表單物件呢?為了獲取表單的例項,我們需要設定一個全域性型別的key,通過這個key的屬性,來獲取表單物件:

 

GlobalKey<FormState> globalKey = new GlobalKey<FormState>();

 

我們來簡單的寫一個登陸頁面,校驗輸入框內的內容,當內容不合法時,並給出相應的提示:

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

void main() => runApp(DemoApp());

class DemoApp extends StatefulWidget{
  @override
  _DemoAppState createState() => new _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
  String userName;
  String userPwd;
  GlobalKey<FormState> globalKey = new GlobalKey<FormState>();
  void check(){
    var loginForm = globalKey.currentState;
    //驗證表單
    if(loginForm.validate()){
      loginForm.save();
      Fluttertoast.showToast(msg: '資訊提交成功',toastLength: Toast.LENGTH_LONG,gravity: ToastGravity.BOTTOM,textColor: Colors.white);
    }
  }
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'From表單Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Form表單Demo'),
          leading: Icon(Icons.menu,size: 30,),
          actions: <Widget>[
            IconButton(icon: Icon(Icons.search),iconSize: 30, onPressed: null)
          ],
        ),
        body: new Column(
          children: <Widget>[
            new Form(
              key: globalKey,
              child: new Column(
                children: <Widget>[
                  new TextFormField(
                    decoration: new InputDecoration(
                      labelText: '請輸入使用者名稱',
                    ),
                    onSaved: (value){
                      userName = value;
                    },
                  ),
                  new TextFormField(
                    decoration: new InputDecoration(
                      contentPadding: EdgeInsets.only(left: 20,top: 10,right: 0,bottom: 0),
                      hintText: '請輸入密碼',
                      hintStyle: new TextStyle(fontSize: 30,color: Colors.amberAccent)
                    ),
                    obscureText: true,
                    validator: (value){
                      return value.length < 6 ? '密碼長度不夠6位' : null;
                    },
                    onSaved: (value){
                      userPwd = value;
                    },
                  )
                ],
              ),
            ),
            new Container(
              margin: new EdgeInsets.symmetric(vertical: 20,horizontal: 0),
              width: 330,
              height: 50,
              child: new SizedBox(
                child: new RaisedButton(
                  onPressed: check,
                  child: new Text(
                    '確定',
                    style: new TextStyle(
                        fontSize: 20,
                        color: Colors.white
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

 

先看一下上面程式碼的效果截圖,然後我會給大家講一下程式碼的內容

                                   

寫的比較醜,大家見諒,主要是因為我想多試試一些屬性怎麼用,總之是要多嘗試嘛~

上面是輸入內容前和輸入內容後的對比圖,從直觀的表象上可以看的出來,使用者名稱輸入框輸入內容後,提示內容被擠到上面去了,而密碼輸入框輸入內容後提示內容則消失了,這是因為兩個輸入框提示語的text的型別使用的不同,使用者名稱:labelText,密碼:hintText,這兩個都有style屬性,都可以對預設的提示內容字型進行屬性設定。在點選確認按鈕後,會呼叫check()方法,在check()方法中提交表單驗證,這時候會觸發密碼輸入框中的validator: (value),從而實現我們密碼輸入框內容的校驗,這個就是TextFromField中的驗證回撥方法,在表單驗證的時候,會先驗證這個方法中的邏輯判斷,如果驗證失敗返回錯誤資訊,如果驗證通過則返回null。


 

接下來的內容有興趣的可以繼續看一下,我想以Android中寫xml的佈局方式理解一下Flutter中的頁面構建,因為我前面一直是在寫簡單的元件Demo,還沒有形成一個完整的構建意識,以下是我個人的理解,如果有不對的地方,還請留言批評、指正,不勝感謝!!!

從整個頁面來構思的話,可以看做是一個大容器,容器裡面有三個元件,分別是兩個輸入框和一個按鈕,這3個元件是垂直方向排列的,寫Android的同學看到這個頁面,很容易就會想到最外層放一個Linearlayout或者是一個RelativeLayout,然後在裡面垂直方向放上兩個EditText和一個Button,再寫一下控制元件的屬性的就完事兒了,其實寫Flutter也是這種思想。

注:在這裡我先說明以下在Flutter中child裡面只能放一個Widget,Children可以放多個Widget。

我們來把Android和Flutter對比著理解,這樣應該可以更容易理解一點,也更直觀一點:

1.Android:最外層垂直方向的Linearlayout或RelativeLayout。

   Flutter:最外層body我們new了一個Column(Column也是一個容器,類似於Container)

2.Android:放上兩個EditText和一個Button

   Flutter:容器放置好了,我們要開始往容器裡面加元件元素了,因為容器裡面我們要放置多個元件,所以我們要使用children,children裡面放一個Form表單和一個button,在Form表單裡面,我們要放兩個輸入框,而且是垂直方向放置的,想到這裡是不是就該先考慮怎麼把方向設定好呢?所以Form表單裡的第一層widget就要放置一個Column容器,容器裡面放兩個輸入框,既然是兩個,那麼是不是兩個輸入框就應該被一個children包裹起來呢?

總的來說:根widget(column)->children(裡面包含Form和button)->Form表單(第一層)->child(column)->children(裡面包含了兩個輸入框TextFormField)->TextFormField(第三層)

 

最後看一下按鈕部分的程式碼,說明一下為什麼外面要包一層Container

new Container(
              margin: new EdgeInsets.symmetric(vertical: 20,horizontal: 0),
              width: 330,
              height: 50,
              child: new SizedBox(
                child: new RaisedButton(
                  onPressed: check,
                  child: new Text(
                    '確定',
                    style: new TextStyle(
                        fontSize: 20,
                        color: Colors.white
                    ),
                  ),
                ),
              ),
            )

之所以最外層包了個Container,是因為SizedBox和RaiseButton這兩個元件都沒有margin或padding屬性,所以UI上要想控制邊距等操作,這是一種處理方式。

 

也不知道我上面大白話寫了那麼多,能不能表達清楚我的意思,如果有沒看懂的,還麻煩留言提問吧!!!

 

下一章節:Flutter學習筆記(14)--App結構和導航元件

 

相關文章