前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 中 TextField 组件必然会遇到的问题

Flutter 中 TextField 组件必然会遇到的问题

作者头像
老孟Flutter
发布2021-11-25 12:59:31
2.8K0
发布2021-11-25 12:59:31
举报
文章被收录于专栏:Flutter

TextField 组件几乎是开发中必然会用到的一个组件,在使用的过程中会遇到两个非常棘手的问题:

  • 字数统计异常。
  • 设置高度,文字无法居中。

字数统计异常

一般情况下,实现字数统计方法如下:

代码语言:javascript
复制
TextField(
  onChanged: (value){
    setState(() {
      _textFieldValue = value;
    });
  },
  decoration: InputDecoration(
      counterText: '${_textFieldValue.length}/32'
  ),
),

大部分情况下是没有问题的,但是在 IOS 简体拼音输入法下有问题(可能其他输入法也有类似的问题),效果如下:

中文输入法统计正在编辑中文的过程中会统计英文,假如限制5个中文,当输入4个中文后,最后一个中文输入2个及以上英文时,会触发最大字数限制,这当然不是我们想要的效果。

❝在去年的时候,这个Bug解决了很久都没有解决,最终产品妥协去掉了这个功能,直到最近查看源码的时候,无意中发现了这个Bug的解决方案。 ❞

下面说下如何修复这个问题,关键是 TextField 中 「controller.value.composing」 这个属性,官方文档说明:

❝The range of text that is still being composed. 仍在编写的文本范围。 ❞

就是上面GIF中出现下划线的部分,字数统计计算:

代码语言:javascript
复制
  TextEditingController _controller = TextEditingController();
  int _wordLength = 0;
  /// 计算字数,不算正在编辑的文字
  void _computeWordCount() {
    var valueLength = _controller.value.text.length;
    var composingLength =
        _controller.value.composing.end - _controller.value.composing.start;
    setState(() {
      _wordLength = valueLength - composingLength;
    });
  }

代码语言:javascript
复制
TextField(
  controller: _controller,
  onChanged: (value){
    _computeWordCount();
  },
  decoration: InputDecoration(
      counterText: '$_wordLength/32'
  ),
),

文字无法居中

首先我们写一个 「TextField」 的基本用法,为了方便定位文字是否居中,给 「TextField」 加上边框:

代码语言:javascript
复制
TextField(
  decoration: InputDecoration(
    enabledBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Color(0xFFDCDFE6)),
      borderRadius: BorderRadius.all(Radius.circular(4.0)),
    ),
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Color(0xFF409EFF)),
      borderRadius: BorderRadius.all(Radius.circular(4.0)),
    ),
  ),
),

此时文字是正好居中的,下面改变 TextField 的高度:

代码语言:javascript
复制
Container(
  height: 30,
  child: TextField(
    decoration: InputDecoration(
      enabledBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFFDCDFE6)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFF409EFF)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
    ),
  ),
),

发现此时文字已经不居中了,当然网上有很多解决办法,比如设置 contentPadding: EdgeInsets.symmetric(vertical: 0,horizontal: 12)

代码语言:javascript
复制
Container(
  height: 30,
  child: TextField(
    decoration: InputDecoration(
      fillColor: Colors.white,
      filled: true,
      enabledBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFFDCDFE6)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFF409EFF)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
      contentPadding: EdgeInsets.symmetric(vertical: 0,horizontal: 12),
    ),
  ),
),

其实这种方式并没有严格居中对齐,只不过偏差较小,勉强可以接受。

看下面的例子,设置高度为150:

代码语言:javascript
复制
Container(
  height: 150,
  color: Colors.green.withOpacity(.5),
  child: TextField(
    decoration: InputDecoration(
      fillColor: Colors.white,
      filled: true,
      enabledBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFFDCDFE6)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Color(0xFF409EFF)),
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
    ),
  ),
)

发现 TextField 的高度不是150,在 「maxLines = 1」 的情况下,通过设置 「contentPadding」 改变其高度,为了方便验证是否居中,在中间绘制一条对齐线:

代码语言:javascript
复制
Container(
  height: 150,
  color: Colors.green.withOpacity(.5),
  child: Stack(
    children: [
      TextField(
        decoration: InputDecoration(
          fillColor: Colors.white,
          filled: true,
          enabledBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Color(0xFFDCDFE6)),
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
          ),
          focusedBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Color(0xFF409EFF)),
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
          ),
          contentPadding:
              EdgeInsets.symmetric(horizontal: 12, vertical: 75),
        ),
      ),
      Positioned.fill(
          child: Divider(
        height: 1,
        color: Colors.red,
      )),
    ],
  ),
),

发现其未居中,调整 「contentPadding」

代码语言:javascript
复制
contentPadding:
    EdgeInsets.symmetric(horizontal: 12, vertical: 67.5)

我们改变文字的大小:

代码语言:javascript
复制
Container(
  height: 150,
  color: Colors.green.withOpacity(.5),
  child: Stack(
    children: [
      TextField(
        decoration: InputDecoration(
          fillColor: Colors.white,
          filled: true,
          enabledBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Color(0xFFDCDFE6)),
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
          ),
          focusedBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Color(0xFF409EFF)),
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
          ),
          contentPadding: EdgeInsets.symmetric(
              horizontal: 12, vertical: 67.5),
        ),
        style: TextStyle(fontSize: 30),
      ),
      Positioned.fill(
          child: Divider(
        height: 1,
        color: Colors.red,
      )),
    ],
  ),
),

此时又不居中了,contentPadding** 需要设置的值是根据 TextField的高度 和 文字高度共同决定的,公式是:

「( TextField的高度 - 文字高度)/2」

我们需要计算出文字的高度:

代码语言:javascript
复制
    TextStyle _style = const TextStyle(fontSize: 30);
    var textPainter = TextPainter(
      text: TextSpan(
        text: '',
        style: _style,
      ),
      textDirection: TextDirection.ltr,
      textWidthBasis: TextWidthBasis.longestLine,
    )..layout();

「textPainter.height」 表示文字的高度。

设置 contentPadding:

代码语言:javascript
复制
contentPadding: EdgeInsets.symmetric(
    horizontal: 12,
    vertical: (150 - textPainter.height) / 2),

以后再也不需要根据不同的高度和字体进行微调了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老孟Flutter 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 字数统计异常
  • 文字无法居中
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档