Flutter iOS输入法 Bug Fix 记录

前言

该文章只记录与工作中碰到的问题,Flutter 版本为 1.2.6 。有更多解决办法欢迎讨论学习。

背景

产品想要在商品搜索界面实现实时搜索。用户在输入商品名称的时候 就自动搜索商品信息。

问题

ios机型在原生键盘中文场景下,在输入字母拼音时onChanged:方法在iOS上会实时回调,拼音也会被搜索。如果输入过快,由于接口异步回调导致内容展示异常。

错误解决办法

  1. 输入中文时限制字母拼音在输入框内的显示,待选择中文后再显示,此时回调onChanged(不推荐使用,商品并非都是中文)
  2. 使用dart自带属性isComposingRangeValid (听说 Flutter 2.0可用)
#state.dart

TextEditingController _controller;

_controller = TextEditingController.fromValue(TextEditingValue(
          text: searchText,
          // 保持光标在最后
          selection: TextSelection.fromPosition(TextPosition(
              affinity: TextAffinity.downstream, offset: searchText.length))))
#View.dart

Expanded(
  flex: 1,
  child: TextField(
    decoration: new InputDecoration(
      hintText: '搜索商品名称、原料、规格、属性',
      border: InputBorder.none,
      isDense: true,
      isCollapsed: false,
    ),
    autofocus: true,
    style: TextStyleMs.ff_333333_16,
    controller: state.controller,
    onChanged: (value) {
      //使用controller 判断
      if (state.controller.value.isComposingRangeValid) {
        return;
      }
      //退出软键盘
      FocusScope.of(viewService.context)
          .requestFocus(state.blankNode);
      dispatch(
          GoodsSearchActionCreator.onUpdateSearchText(
              value));
      dispatch(GoodsSearchActionCreator.onSearchFoods(
          value));
    },
  ),
),

实测: 该方法返回的isComposingRangeValid 针对ios判断依然是 true,没有效果

正确解决思路

/// Builds [TextSpan] from current editing value.
  ///
  /// By default makes text in composing range appear as underlined. Descendants
  /// can override this method to customize appearance of text.
  TextSpan buildTextSpan({TextStyle style , bool withComposing}) {
    assert(!value.composing.isValid || !withComposing || value.isComposingRangeValid);
    // If the composing range is out of range for the current text, ignore it to
    // preserve the tree integrity, otherwise in release mode a RangeError will
    // be thrown and this EditableText will be built with a broken subtree.
    // 注释1
    if (!value.isComposingRangeValid || !withComposing) {
      return TextSpan(style: style, text: text);
    }
    final TextStyle composingStyle = style.merge(
      const TextStyle(decoration: TextDecoration.underline),
    );
        // 注释2
    return TextSpan(
      style: style,
      children: <TextSpan>[
        TextSpan(text: value.composing.textBefore(value.text)),
        TextSpan(
          style: composingStyle,
          text: value.composing.textInside(value.text),
        ),
        TextSpan(text: value.composing.textAfter(value.text)),
    ]);
  }

通过查看TextEditingController源码,看到源码已经对输入内容做了一定的判断

  1. 通过来判断 value.composing ,第一个判断是直接输入的,那就直接返回一个普通样式
  2. value.composing.textInside(value.text)获取到的就是输入未完成的字符,默认是添加了一个下划线的样式(composingStyle)。根据注释,google提示我们可以重写此方法改写样式!

正确解决办法

新建一个controller继承自TextEditingController,重写buildTextSpan方法

class ChinaTextEditController extends TextEditingController{
  ///拼音输入完成后的文字
  var completeText = '';

  @override
  TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
    ///拼音输入完成
    if (!value.composing.isValid || !withComposing) {
      if(completeText!=value.text){
        completeText = value.text;
        WidgetsBinding.instance.addPostFrameCallback((_){
          notifyListeners();
        });
      }
      return TextSpan(style: style, text: text);
    }

    ///返回输入样式,可自定义样式
    final TextStyle composingStyle = style.merge(
      const TextStyle(decoration: TextDecoration.underline),
    );
    return TextSpan(
        style: style,
        children: <TextSpan>[
          TextSpan(text: value.composing.textBefore(value.text)),
          TextSpan(
            style: composingStyle,
            text:
            value.composing.isValid && !value.composing.isCollapsed?
            value.composing.textInside(value.text):"",
          ),
          TextSpan(text: value.composing.textAfter(value.text)),
        ]);
  }

}

redux 的view 中使用

Expanded(
  flex: 1,
  child: TextField(
    decoration: new InputDecoration(
      hintText: '搜索商品名称、原料、规格、属性',
      border: InputBorder.none,
      isDense: true,
      isCollapsed: false,
    ),
    autofocus: true,
    style: TextStyleMs.ff_333333_16,
    controller: state.controller,
  ),
),

redux state中绑定

class GoodsSearchState implements Cloneable<GoodsSearchState> {

  ...

  ChinaTextEditController _controller;

  ChinaTextEditController get controller => _controller;

  set controller(TextEditingController value) {
    _controller = value;
  }

  ///空白焦点 用来隐藏软键盘
  FocusNode blankNode;

  @override
  GoodsSearchState clone() {
    return GoodsSearchState()
      ..searchText = searchText
      .._controller = _controller
      ...
            ;
  }
}

GoodsSearchState initState(Map<String, dynamic> args) {
  GoodsSearchState state = GoodsSearchState();
  state.searchText = '';
  state.blankNode = FocusNode();
  state.controller = ChinaTextEditController();
  state.controller.text = state.searchText;
    //输入下标在文字之后
  state.controller.selection = TextSelection.fromPosition(TextPosition(
      affinity: TextAffinity.downstream, offset: state.searchText.length));
  ...
  return state;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容