flutter输入框运用的是TextField,能够经过设置它的特点给输入框和内部文字设置不同的款式,之前看了一个日记APP,是这样的:

flutter输入框输入多种样式的实现思路

能够给输入的文字别离设置款式。

于是就想用flutter去完成这样的作用。查了一下资料发现,TextField中有controller特点,需要传入TextEditingController,它能够去监听text的一些变化以及设置TextField中光标的方位等,最重要的是,能够自己写子类承继TextEditingController重写buildTextSpan办法,该办法返回TextSpan组件,flutter中的富文本组件RichText就是运用TextSpan来完成的。TextSpan中有children,咱们只需要在buildTextSpan办法中拼接children,给children中的元素装备不同的TextStyle就能够完成这样的作用。

如这儿写了一个子类重写了buildTextSpan办法,在这个办法中,经过每次text的变化,更新文本中的款式段数组,经过款式段数组组成TextSpan来制作输入框文本。

class RichTextEditingController extends TextEditingController {
  TextStyle _curStyle = TextStyle();
  void setCurStyle(TextStyle style) {
    _curStyle = style;
  }
  TextSegments configs = TextSegments();
  @override
  TextSpan buildTextSpan(
      {required BuildContext context,
      TextStyle? style,
      required bool withComposing}) {
    assert(!value.composing.isValid ||
        !withComposing ||
        value.isComposingRangeValid);
    //给TextSegments装备数据更新replacements
    configs.config(
        selectRange: value.selection,
        editingRange: value.composing,
        text: value.text,
        style: _curStyle);
    List<InlineSpan> children = [];
    //拼接
    for (TextSegment element in configs.replacements) {
      TextSpan span = TextSpan(text: element.text, style: element.style);
      children.add(span);
    }
    print(
        "selectRange: {${value.selection.start},${value.selection.end}}, text: ${text},{${configs.replacements}}");
    return TextSpan(children: children);
  }
}

TextSegments中的replacements是TextSegment数组,TextSegment中记录了每个文本款式段的文本信息和文本坐标规模:

class TextSegment {
  TextRange range;
  TextStyle style;
  String text;
  TextSegment({required this.range, required this.style, required this.text});
  //自己在不在挑选的区域中
  bool inRange(TextRange selectRange) {
    if (text.length <= 1) return false;
    return rangeContain(range, selectRange.start) ||
        rangeContain(range, selectRange.end);
  }
  省掉的代码.....
}

TextSegments经过text的改动对内部的款式段数组进行增删改操作:

class TextSegments {
  List<TextSegment> replacements = [];
  String oldText = "";
  //上一次光标选中的区域
  TextRange selectRange = TextRange(start: -1, end: -1);
  TextRange editingRange = TextRange(start: -1, end: -1);
  void config(
      {required TextRange selectRange,
      required TextRange editingRange,
      required String text,
      required TextStyle style}) {
    if (oldText == text) {
      return;
    }
    //删去
    if (text.length < oldText.length) {
      delete(selectRange, editingRange, text);
    }
    //直接append到最后边
    else if (selectRange.start > oldText.length || replacements.length == 0) {
      append(selectRange, editingRange, text, style);
    }
    //从中心刺进
    else {
      insert(selectRange, editingRange, text, style);
    }
    oldText = text;
  }
  .....下面省掉代码.....
}

对replacements进行删去和刺进的时分,一定要重新更新刺进和删去之后的元素的方位信息TextRange。如删去的时分:

void _deleteWithStart(int start) {
    int idx = -1;
    for (var i = 0; i < replacements.length; i++) {
      TextSegment element = replacements[i];
      if (element.removeWithStart(start)) {
        idx = i;
      } else if (idx != -1) {
        //更新后边元素的方位信息
        element.range = TextRange(
            start: element.range.start - 1, end: element.range.end - 1);
      }
    }
    if (idx != -1 && replacements[idx].isEmpty()) {
      replacements.removeAt(idx);
    }
  }

刺进的时分:

void insert(TextRange selectRange, TextRange editingRange, String text,
      TextStyle style) {
    int insertIndex = -1;
    ....省掉代码,找到需要刺进的方位insertIndex....
    //找到需要将增加的文字刺进到replacements的某个元素中
    for(){
    ... insertIndex = ...
    }
    //找到需要将增加的文字刺进到replacements的某个元素后边
    if(insertIndex = -1) {
        for(){
            ... insertIndex = ...
        }
    }
    //都没找见,直接append到最后边
    if (insertIndex == -1) {
      append(selectRange, editingRange, text, style);
      return;
    }
    //将增加的文字参加进来
    TextRange insertR = TextRange(
        start: selectRange.start,
        end: selectRange.start + (text.length - oldText.length));
    TextSegment newT = TextSegment(
        range: insertR,
        style: style,
        text: text.substring(insertR.start, insertR.end));
    list.insert(insertIndex + 1, newT);
     //刺进的range后边的元素,range后移
    for (var i = insertIndex + 2; i < replacements.length; i++) {
      TextSegment element = replacements[i];
      element.range = TextRange(
          start: element.range.start + length, end: element.range.end + length);
    }
    replacements = list;
    _connect();  //将相同style切相邻的元素进行兼并
  }

最后简单的完成作用:

flutter输入框输入多种样式的实现思路