Flutter UI - 表單系 Widget

前行的烏龜發表於2019-11-30

Flutter UI - 表單系 Widget

包括以下 widget:

  • TextField - 輸入框
  • Switch - 選擇切換
  • Radio - 單選按鈕組
  • Checkbox - 多選按鈕組
  • ``
  • ``
  • ``
  • ``
  • ``

TextField

網上的資料包括官方文件都是一上來一大堆屬性列出來,輸入框可設定的屬性太多了,這樣的話很難看的,很勸退的,所以我努力把這些屬性分門別類按照幾個大的範圍整理下,以往能看的順眼

屬性大全

  // 雖然不想寫,但是還是寫上吧
  const TextField({
    Key key,
    this.controller,    //編輯框的控制器,跟文字框的互動一般都通過該屬性完成,如果不建立的話預設會自動建立
    this.focusNode,  //用於管理焦點
    this.decoration = const InputDecoration(),   //輸入框的裝飾器,用來修改外觀
    TextInputType keyboardType,   //設定輸入型別,不同的輸入型別鍵盤不一樣
    this.textInputAction,   //用於控制鍵盤動作(一般位於右下角,預設是完成)
    this.textCapitalization = TextCapitalization.none,
    this.style,    //輸入的文字樣式
    this.textAlign = TextAlign.start,   //輸入的文字位置
    this.textDirection,    //輸入的文字排列方向,一般不會修改這個屬性
    this.autofocus = false,   //是否自動獲取焦點
    this.obscureText = false,   //是否隱藏輸入的文字,一般用在密碼輸入框中
    this.autocorrect = true,   //是否自動校驗
    this.maxLines = 1,   //最大行
    this.maxLength,   //能輸入的最大字元個數
    this.maxLengthEnforced = true,  //配合maxLength一起使用,在達到最大長度時是否阻止輸入
    this.onChanged,  //輸入文字發生變化時的回撥
    this.onEditingComplete,   //點選鍵盤完成按鈕時觸發的回撥,該回撥沒有引數,(){}
    this.onSubmitted,  //同樣是點選鍵盤完成按鈕時觸發的回撥,該回撥有引數,引數即為當前輸入框中的值。(String){}
    this.inputFormatters,   //對輸入文字的校驗
    this.enabled,    //輸入框是否可用
    this.cursorWidth = 2.0,  //游標的寬度
    this.cursorRadius,  //游標的圓角
    this.cursorColor,  //游標的顏色
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.dragStartBehavior = DragStartBehavior.down,
    this.enableInteractiveSelection,
    this.onTap,    //點選輸入框時的回撥(){}
    this.buildCounter,
  })
複製程式碼

鍵盤控制

1. 鍵盤樣式

Flutter 內建了不少種鍵盤樣式的,有的我也沒搞明白,就這麼看吧

  • TextInputType.text - 普通完整鍵盤
  • TextInputType.number - 數字鍵盤
  • TextInputType.emailAddress - 帶有“@”的普通鍵盤
  • TextInputType.datetime - 帶有“/”和“:”的數字鍵盤)
  • TextInputType.multiline - 帶有選項以啟用有符號和十進位制模式的數字鍵盤
  • url - 會顯示“/ .”
  • phone - 會彈出數字鍵盤並顯示"* #"
 TextField(
   keyboardType: TextInputType.datetime,
 )
複製程式碼

2. 大小寫控制

  • TextCapitalization.none - 全部小寫
  • TextCapitalization.words - 每個單詞的首字母大寫
  • TextCapitalization.sentences - 每個句子的首字母大寫
  • TextCapitalization.characters - 每個字每大寫

我試了試,有的鍵盤貌似不管用,大家還是自己靠正則或是校驗來控制輸入吧,感覺這個設定不靠譜

3. 修改鍵盤動作按鈕樣式

關鍵詞是 textInputAction,改變的是鍵盤右下角那個按鈕的樣式,根據安裝的輸入法的不同,有的顯示的是文字,有的顯示的是圖示。Flutter 內建了不少樣式,可惜沒有預覽,只能看英文自己猜了

TextField(
  textInputAction: TextInputAction.search,
),
複製程式碼

Flutter UI - 表單系 Widget

4. 鍵盤動作按鈕點選響應 - 無參的

回撥是 onEditingComplete,注意是沒有返回值的

TextField(
  onEditingComplete: (){
    print("AA");
  },
),
複製程式碼

5. 鍵盤動作按鈕點選響應 - 有參的

回撥是 onSubmitted,這次是有返回值的,也就是你輸入的資料

TextField(
  onSubmitted: (inputValue){
    print("輸入資料位: ${inputValue}");
  },
),
複製程式碼

監聽事件

  • onChanged - 當使用者輸入就會觸發回撥,我們從中可以拿到使用者輸入值,經過測試,每次拿到的都是使用者所有的輸入值
TextField(
  keyboardType: TextInputType.datetime,
    onChanged: (inputValue) {
      print("input:${inputValue}");       
    },
)
複製程式碼
  • TextEditingController - 官文件描述為:文字控制器,目前發現也就是 TextEditingController.text 拿到輸入的內容,Flutter widget 一般都不要用 物件.屬性的去寫,所以提供了這麼一個東西。我們要 new 一個 TextEditingController 出來,傳給 TextField
class TestWidgetState extends State<TestWidget> {
  var editContore = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          width: 200,
          child: TextField(
            keyboardType: TextInputType.datetime,
            onChanged: (inputValue) {
              print("input:${inputValue}");
            },
            controller: editContore,
          ),
        ),
        RaisedButton(
          child: Text("請點選"),
          onPressed: () {
            print("輸入內容:${editContore.text}");
          },
        ),
      ],
    );
  }
}
複製程式碼
  • onTap - 在按下開始輸入的那一瞬間觸發,每次輸入只觸發一次
TextField(
  keyboardType: TextInputType.datetime,
    onTap: (){              
      print("tap...");
    },
)
複製程式碼
  • onEditingComplete - 鍵盤完成按鈕響應 - 無引數的
  • onSubmitted - 鍵盤完成按鈕響應 - 有引數的
  • 實現右邊圖示點選 - 預設沒有回撥,需要我們自己寫
class TestWidgetState extends State<TestWidget> {
  var isCan = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          width: 200,
          child: TextField(
            decoration: InputDecoration(
              suffixIcon: isCan
                  ? GestureDetector(
                      child: Icon(Icons.clear),
                      onTap: () {
                        print("AAAAAA");
                      },
                    )
                  : null,
              suffixText: isCan ? ":請確認" : null,
              suffixStyle: TextStyle(fontSize: 16),
            ),
          ),
        ),
      ],
    );
  }
}
複製程式碼

輸入框樣式

Flutter 裡修改樣式啥的基本都是 decoration,contain 裡是 BoxDecoration,TextField 的則是 InputDecoration,這部分內容也是相當多的,我拆開來說,我喜歡分門別類

1. 提示文字:

  • labelText/labelStyle - 輸入框標題吧,在輸入內容的上面,在我們輸入之後,labelText 會有一個縮放的動畫的,style 是文字樣式,注意這個文字樣式只能在輸入之前管事
  • helperText/helperStyle - 輸入內容下部的提示文字
  • errorText/errorStyle - 錯誤提示文字,也是在輸入內容下部
  • hintText/hintStyle - 預設提示文字
    Flutter UI - 表單系 Widget
    Flutter UI - 表單系 Widget
TextField(
  labelText: "姓名:",
  labelStyle: TextStyle(
    fontSize: 22,
  ),
  helperText: '請輸入你的真實姓名',
  helperStyle: TextStyle(
    fontSize: 8,
  ),
  errorText: "請重新輸入",
  errorStyle: TextStyle(
    fontSize: 12,
  ),
  hintText: "請輸入文字",
  hintStyle: TextStyle(
    fontSize: 12,
  ),
)
複製程式碼

2. 左右提示和圖片

  • prefixIcon/prefixText/prefixStyle - 左邊的圖示和文字,樣式看下面圖樣,提示文字不會進入到輸入內容的,這個測試過了
  • suffixIcon/suffixText/suffixStyle - 右邊的圖示和文字
  • prefix/suffix - 或者直接自己給一個 widget 也可以
  • icon - 左邊的圖示,但是圖示在輸入欄的外面,樣子看下面
  • 注意點 - 這裡的提示文字是隻有在輸入框拿到焦點時才會顯示,否則只會顯示 hint 文字
    Flutter UI - 表單系 Widget
    Flutter UI - 表單系 Widget
    Flutter UI - 表單系 Widget
  • suffix 點選 - 預設沒提供相關回撥,我們自己套一個 GestureDetector 就行啦,測試過沒問題的,並且不會和 onTap 衝突的
class TestWidgetState extends State<TestWidget> {
  var editContore = TextEditingController();
  var isCan = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          width: 200,
          child: TextField(
            decoration: InputDecoration(
              icon: Icon(Icons.search),
              prefixIcon: isCan ? Icon(Icons.access_alarm) : null,
              prefixText: isCan ? "輸入:" : null,
              prefixStyle: TextStyle(fontSize: 12),
              suffixIcon: isCan
                  ? GestureDetector(
                      child: Icon(Icons.clear),
                      onTap: () {
                        print("AAAAAA");
                      },
                    )
                  : null,
              suffixText: isCan ? ":請確認" : null,
              suffixStyle: TextStyle(fontSize: 16),
            ),
          ),
        ),
      ],
    );
  }
}
複製程式碼

3. 右小角統計數字

這個具體的文字樣式是自己做的,假如想要 6/10 這種樣式的,需要自己 setState 去修改文字

  • counterText - 文字
  • counterStyle - 文字樣式
  • counter - 自定義 widget
    Flutter UI - 表單系 Widget
  • 要是想達到圖中的效果:
var count = 0;

TextField(
  decoration: InputDecoration(
    counterText: "${count}/10",
  ),
  onChanged: (inputValue) {
    setState(() {
      count = inputValue.length;
    });
  },
)
複製程式碼

4. 改顏色

  • filled/fillColor - 修改輸入框背景色,filled 是個 boolean 值,用來控制顏色顯示的
TextField(
  decoration: InputDecoration(
    filled: true,
    fillColor: Colors.grey,
  ),
)
複製程式碼

Flutter UI - 表單系 Widget

5. 修改邊框

邊框有3種:

  • InputBorder.none - 沒有邊框,此模式下連下面的線都沒有了
  • OutlineInputBorder - 4面邊框,可以設定圓角
TextField(
  decoration: InputDecoration(
    enabledBorder: OutlineInputBorder(
      borderRadius: BorderRadius.all(
      Radius.circular(30), //邊角為30
      ),
      borderSide: BorderSide(
        color: Colors.amber, //邊線顏色為黃色
        width: 2, //邊線寬度為2
      ),
    ),
)
複製程式碼
  • UnderlineInputBorder - 底邊線,一樣可以設定圓角,不過不好看
TextField(
  decoration: InputDecoration(
    enabledBorder: UnderlineInputBorder(
      borderRadius: BorderRadius.all(
        Radius.circular(30), //邊角為30
      ),
      borderSide: BorderSide(
        color: Colors.amber, //邊線顏色為黃色
        width: 2, //邊線寬度為2
      ),
    ),
)
複製程式碼

輸入驗證

就是 TextInputFormatter 這個東西啦,他是一個抽象介面,具體實現有以下幾種:

  • WhitelistingTextInputFormatter - 白名單校驗,也就是隻允許輸入符合規則的字元
  • BlacklistingTextInputFormatter - 黑名單校驗,除了規定的字元其他的都可以輸入
  • LengthLimitingTextInputFormatter - 長度限制,跟maxLength作用類似

效果呢就是輸入無效,你輸入的非法字元既無法顯示,也不被算作輸入之內

inputFormatters: [WhitelistingTextInputFormatter(RegExp("[a-z]"))],
複製程式碼

inputFormatters 的特點呢,就是可以接受多個格式驗證啦

  • 首先 inputFormatters 接受的是 [] 集合引數
  • 再者其實現類,像 WhitelistingTextInputFormatter 他們接受的是 Pattern 型別的資料,而正則 RegExp 恰恰就實現了 Pattern,所以本質上接受的就是正規表示式

當然了,使用 inputFormatters 的話是沒辦法和 errorText 聯動的啦,如果要有這樣的效果,那麼就得自己在 onChange 中自己做正則判斷啦


Switch

Switch 不多做介紹,大家都知道的,flutter 的這個 Switch 可以設定選中時按鈕的顏色,圖示,橫條的顏色:

  • activeColor - 選中時按鈕的顏色
  • activeThumbImage - 選中時按鈕的圖示
  • activeTrackColor - 選中時和橫條的顏色
  • inactiveThumbColor - 不選時按鈕的顏色
  • inactiveTrackColor - 不選時橫條的顏色
  • inactiveThumbImage - 不選時按鈕的圖示
  • onChanged - 點選影響
  • value - 當前值,這個必須一開始就寫上哈

Switch 在這裡的最大問題就是此存有點小,大家看下圖,這是相對整個螢幕的大小,而且 Switch 本身還不支援大小調整

Flutter UI - 表單系 Widget

class TestWidgetState extends State<TestWidget> {
  bool _value = false;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      width: 200,
      decoration: BoxDecoration(
        color: Colors.tealAccent,
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text('關'),
          Switch(
            value: _value,
            activeColor: Colors.blue,
            activeTrackColor: Colors.green,
            activeThumbImage: AssetImage(('assets/icons/icon_1.gif')),
            inactiveThumbColor: Colors.red,
            inactiveThumbImage: AssetImage(('assets/icons/icon_2.jpg')),
            inactiveTrackColor: Colors.grey,
            onChanged: (newValue) {
              setState(() {
                _value = newValue;
              });
            },
          ),
          Text('開'),
        ],
      ),
    );
  }
}
複製程式碼

Radio

Flutter UI - 表單系 Widget

Radio 是單選按鈕,我總是和多選混。Flutter 的單選按鈕設計挺簡單的,使用 groupValue 參數列示一組單選按鈕的統一 ID,用相同 groupValue 的單選按鈕就算一組的,比在外面再加一層容器的思路靈活多了,這樣頁面好寫多了

  • value - 單選按鈕值,這裡值是無實際意義,此值就是表示使用者點個哪個按鈕,配合列舉會好用很多
  • groupValue - 單選按鈕組統一 ID
  • onChanged - 回撥
  • activeColor - 選中時候的顏色
class TestWidgetState extends State<TestWidget> {
  String _value = '1';
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Radio(
                value: 'a',
                activeColor: Colors.blue,
                groupValue: _value,
                onChanged: (newValue) {
                  setState(() {
                    print("a:${newValue}");
                    _value = newValue;
                  });
                }
            ),
            Text('開')
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Radio(
                value: 'b',
                activeColor: Colors.blue,
                groupValue: _value,
                onChanged: (newValue) {
                  setState(() {
                    print("b:${newValue}");
                    _value = newValue;
                  });
                }
            ),
            Text('關')
          ],
        )
      ],
    );
  }
}
複製程式碼

Checkbox

Flutter UI - 表單系 Widget

Checkbox Flutter 的一貫設計:書寫簡潔設計簡單,多選按鈕組也是沒有外層容器那一說了,而是採用有多少個按鈕選項就配合多少個 boolean 引數的思路,配合上表驅動法的思路,很 Nice

  • value - true 選中,fasle 不選中
  • activeColor - 選中時填充顏色
  • checkColor - 選中時 Icon 顏色
class TestWidgetState extends State<TestWidget> {
  var _isBook = false;
  var _isPrice = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Checkbox(
              value: _isBook,
              onChanged: (newValue) {
                print('book:$newValue');
                setState(() {
                  _isBook = newValue;
                });
              },
              activeColor: Colors.red,
              checkColor: Colors.blue,
            ),
            Text("書"),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Checkbox(
              value: _isPrice,
              onChanged: (newValue) {
                print('price:$newValue');
                setState(() {
                  _isPrice = newValue;
                });
              },
              activeColor: Colors.red,
              checkColor: Colors.blue,
            ),
            Text("價格"),
          ],
        )
      ],
    );
  }
}
複製程式碼

相關文章