欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter 如何封裝文本輸入框組件

 更新時間:2021年06月05日 11:12:48   作者:島上碼農(nóng)  
在實際開發(fā)過程中,往往開始是完成功能層面的開發(fā),然而再考慮組件封裝和代碼優(yōu)化。當(dāng)然,組件封裝越早做越好,因為這樣會提高整個團(tuán)隊開發(fā)的規(guī)范性和效率。本文將介紹如何封裝一個文本輸入框組件

UI組件封裝的考慮要點

封裝一個 UI 組件,通常需要考慮下面這三個點:

  1. 接口如何定義:即組件接收什么輸入?yún)?shù)來控制組件的外觀和行為;
  2. 與業(yè)務(wù)分離:UI 組件應(yīng)當(dāng)只負(fù)責(zé)界面,而不負(fù)責(zé)業(yè)務(wù),具體的業(yè)務(wù)應(yīng)當(dāng)由業(yè)務(wù)層完成;
  3. 簡單易用:因為是 UI 組件,要盡可能地簡單易用,方便使用者快速上手。

文本輸入框接口定義

首先先看一下我們上一篇使用文本框的代碼,這里實際上只是調(diào)用了一個函數(shù)返回新的組件,之所以要這么做是因為用戶名、密碼使用了成員屬性,需要根據(jù)不同的成員屬性來設(shè)置不同的行為,主要有:

  • 文本框裝飾不同:包括占位符、前置圖標(biāo),后置圖標(biāo)的行為綁定了成員屬性以及不同的 TextEditingCongtroller。
  • onChanged 事件回調(diào)內(nèi)容不同。
  • 鍵盤類型和是否隱藏輸入內(nèi)容不同。
  • 對應(yīng)表單的字段不同。
Widget _getPasswordInput() {
    return _getInputTextField(
      TextInputType.text,
      obscureText: true,
      controller: _passwordController,
      decoration: InputDecoration(
        hintText: "輸入密碼",
        icon: Icon(
          Icons.lock_open,
          size: 20.0,
        ),
        suffixIcon: GestureDetector(
          child: Offstage(
            child: Icon(Icons.clear),
            offstage: _password == '',
          ),
          onTap: () {
            this.setState(() {
              _password = '';
              _passwordController.clear();
            });
          },
        ),
        border: InputBorder.none,
      ),
      onChanged: (value) {
        this.setState(() {
          _password = value;
        });
      },
    );
  }

而實際造成差別的原因是成員屬性之間的差異不同,如果是繼續(xù)使用成員屬性這種方式,無法解決這個問題。這時候我們可以考慮把整個表單放入一個 Map 中,Map里配置不同字段對應(yīng)的差異化屬性,然后就可以做到通用的接口了,我們可以定義封裝后的組件接口:

Widget _getInputTextFieldNew(
    String formKey,
    String value, {
    TextInputType keyboardType = TextInputType.text,
    FocusNode focusNode,
    controller: TextEditingController,
    onChanged: Function,
    String hintText,
    IconData prefixIcon,
    onClear: Function,
    bool obscureText = false,
    height = 50.0,
    margin = 10.0,
  }) {
  //......
}

新增的參數(shù)如下:

  • formKey:表示文本框?qū)?yīng)的是表單Map的哪個鍵;
  • value:當(dāng)前表單的值,用于控制是否顯示清除按鈕
  • onClear:定義右側(cè)清除按鈕的行為響應(yīng)
  • onChanged:輸入內(nèi)容變比回調(diào)

代碼實現(xiàn)

抽離后的代碼與業(yè)務(wù)頁面無關(guān),因此需要抽離代碼,在 lib 目錄下新增一個 components 目錄,增加一個 form_util.dart 文件,用于存放通用的表單組件。實現(xiàn)的代碼如下所示:

class FormUtil {
  static Widget textField(
    String formKey,
    String value, {
    TextInputType keyboardType = TextInputType.text,
    FocusNode focusNode,
    controller: TextEditingController,
    onChanged: Function,
    String hintText,
    IconData prefixIcon,
    onClear: Function,
    bool obscureText = false,
    height = 50.0,
    margin = 10.0,
  }) {
    return Container(
      height: height,
      margin: EdgeInsets.all(margin),
      child: Column(
        children: [
          TextField(
              keyboardType: keyboardType,
              focusNode: focusNode,
              obscureText: obscureText,
              controller: controller,
              decoration: InputDecoration(
                hintText: hintText,
                icon: Icon(
                  prefixIcon,
                  size: 20.0,
                ),
                border: InputBorder.none,
                suffixIcon: GestureDetector(
                  child: Offstage(
                    child: Icon(Icons.clear),
                    offstage: value == null || value == '',
                  ),
                  onTap: () {
                    onClear(formKey);
                  },
                ),
              ),
              onChanged: (value) {
                onChanged(formKey, value);
              }),
          Divider(
            height: 1.0,
            color: Colors.grey[400],
          ),
        ],
      ),
    );
  }
}

組件使用

首先是使用 Map 定義當(dāng)前頁面的表單內(nèi)容,以便控制不同表單字段的呈現(xiàn)形式。

Map<String, Map<String, Object>> _formData = {
    'username': {
      'value': '',
      'controller': TextEditingController(),
      'obsecure': false,
    },
    'password': {
      'value': '',
      'controller': TextEditingController(),
      'obsecure': true,
    },
  };

其次是定義統(tǒng)一的文本框 onChanged 和 onClear 方法,對應(yīng)為 _handleTextFieldChanged和_handleClear。通過 formKey 回傳的字段,可以更新對應(yīng) _formData 的內(nèi)容。這里注意使用了 as用法用于將一個 Object 轉(zhuǎn)換為TextEditingController。這種轉(zhuǎn)換如果 Object 對應(yīng)的類型是TextEditingController的話能夠成功轉(zhuǎn)換,也能正常執(zhí)行后面的 clear()方法。但是如果是 null,直接這時候執(zhí)行 clear()方法,會報空指針異常。因此在轉(zhuǎn)換結(jié)果后面加了個問號,這個表示是如果是 null 后面的方法不會執(zhí)行,從而不會出現(xiàn)空指針異常。這是 Flutter 2.0引入的 null safety 特性。其實這個特效在 PHP 7,Swift 語言早就有應(yīng)用了。

_handleTextFieldChanged(String formKey, String value) {
    this.setState(() {
      _formData[formKey]['value'] = value;
    });
  }

  _handleClear(String formKey) {
    this.setState(() {
      _formData[formKey]['value'] = '';
      (_formData[formKey]['controller'] as TextEditingController)?.clear();
    });
  }

之后是在使用 textField 的地方使用 FormUtil.textField 方法直接使用封裝好的文本框:

//...
FormUtil.textField(
    'username',
    _formData['username']['value'],
    controller: _formData['username']['controller'],
    hintText: '請輸入手機(jī)號',
    prefixIcon: Icons.mobile_friendly,
    onChanged: _handleTextFieldChanged,
    onClear: _handleClear,
  ),
FormUtil.textField(
    'password',
    _formData['password']['value'],
    controller: _formData['password']['controller'],
    obscureText: true,
    hintText: '請輸入密碼',
    prefixIcon: Icons.lock_open,
    onChanged: _handleTextFieldChanged,
    onClear: _handleClear,
),
//...

可以看到,username和 password 兩個表單字段復(fù)用了_handleTextFieldChanged和_handleClear。整個代碼長度也減少了近50%,而且封裝的 FormUtil.textField 文本框也可以用于其他表單頁面,整個代碼的維護(hù)性和復(fù)用性都相比前一篇的有了很大的提高。

踩坑記錄

在封裝 文本框的時候,直接將 onClear 函數(shù)復(fù)制給了GesureDetector 的 onTap 屬性,結(jié)果每次輸入都會觸發(fā) onClear自動清空文本框內(nèi)容。后來發(fā)現(xiàn)實際應(yīng)該是傳一個回調(diào)函數(shù),下面列出了兩種方式的不同:

//...
//錯誤的方式
onTap:onClear,
//...

//...
//正確的方式
onTap:() {
  onClear(formKey);
},
//...

總結(jié)

封裝UI 組件在實際開發(fā)過程中非常常見,一般來說當(dāng)我們看到設(shè)計稿的時候,首先是將設(shè)計稿拆分成多個組件,然后考慮一下其中的一些組件是不是在其他場合也會用到。如果有可能用到,就可以考慮封裝。封裝的時候先考慮對外接口參數(shù),然后注意UI 組件不應(yīng)該涉及到業(yè)務(wù),再就是盡可能地簡便(比如有一些默認(rèn)值,減少必傳參數(shù))。當(dāng)然,如果公司在一開始就能夠由產(chǎn)品、設(shè)計和開發(fā)一起定一套組件,提前封裝將會使得后面的開發(fā)效率更高,但這取決于項目時間的緊急程度,時間充裕的話可以這么考慮。

以上就是Flutter 如何封裝文本輸入框的詳細(xì)內(nèi)容,更多關(guān)于Flutter 封裝文本輸入框的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論