Problems encountered by TextField component in fluent

TextField component is almost a component that will be used in development. Two very difficult problems will be encountered in the process of use:

  • Word count exception.
  • Text cannot be centered because the height is set.

Abnormal word count

Generally, the method of word count is as follows:

TextField(
  onChanged: (value){
    setState(() {
      _textFieldValue = value;
    });
  },
  decoration: InputDecoration(
      counterText: '${_textFieldValue.length}/32'
  ),
),

There is no problem in most cases, but there is a problem in IOS simplified Pinyin input method (other input methods may have similar problems). The effects are as follows:

Chinese input method statistics will count English in the process of editing Chinese. If five Chinese are limited, the maximum word limit will be triggered when the last Chinese is input with two or more English after four Chinese are input. Of course, this is not the effect we want.

❝ last year, the Bug was solved for a long time, but the function was removed in the final product compromise. Until recently, when I checked the source code, I accidentally found the Bug solution. ❞

The key to how to fix this problem is the attribute "controller. Value. Composition" in TextField. The official document describes:

❝The range of text that is still being composed. Scope of text still being written. ❞

This is the underlined part in the GIF above. Word count calculation:

  TextEditingController _controller = TextEditingController();
  int _wordLength = 0;
  ///Count the number of words, not the text being edited
  void _computeWordCount() {
    var valueLength = _controller.value.text.length;
    var composingLength =
        _controller.value.composing.end - _controller.value.composing.start;
    setState(() {
      _wordLength = valueLength - composingLength;
    });
  }

TextField(
  controller: _controller,
  onChanged: (value){
    _computeWordCount();
  },
  decoration: InputDecoration(
      counterText: '$_wordLength/32'
  ),
),

Text cannot be centered

First, let's write a basic usage of "TextField". In order to locate whether the text is centered, add a border to "TextField":

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)),
    ),
  ),
),

At this time, the text is just centered. Change the height of TextField below:

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)),
      ),
    ),
  ),
),

It is found that the text is not centered at this time. Of course, there are many solutions on the Internet, such as setting contentPadding: EdgeInsets.symmetric(vertical: 0,horizontal: 12):

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),
    ),
  ),
),

In fact, this method is not strictly centered alignment, but the deviation is small, which is barely acceptable.

Take the following example and set the height to 150:

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)),
      ),
    ),
  ),
)

It is found that the height of TextField is not 150. When "maxLines = 1", change its height by setting "contentPadding". In order to verify whether it is centered, draw an alignment line in the middle:

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,
      )),
    ],
  ),
),

Found that it was not in the middle and adjusted "contentPadding".

contentPadding:
    EdgeInsets.symmetric(horizontal: 12, vertical: 67.5)

We change the size of the text:

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,
      )),
    ],
  ),
),

At this time, it is not centered. The value to be set for contentPadding * * is determined jointly according to the height of TextField and text height. The formula is:

❝ "(TextField height - text height) / 2" ❞

We need to calculate the height of the text:

    TextStyle _style = const TextStyle(fontSize: 30);
    var textPainter = TextPainter(
      text: TextSpan(
        text: '',
        style: _style,
      ),
      textDirection: TextDirection.ltr,
      textWidthBasis: TextWidthBasis.longestLine,
    )..layout();

"textPainter.height" indicates the height of the text.

Set contentPadding:

contentPadding: EdgeInsets.symmetric(
    horizontal: 12,
    vertical: (150 - textPainter.height) / 2),

There is no need to fine tune according to different heights and fonts in the future.

Posted on Fri, 26 Nov 2021 08:56:08 -0500 by Zamees