Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请问如何实现 KeyCodes.BACKSPACE 键的回退删除一个字符或表情? #104

Closed
eronakoto opened this issue Dec 2, 2020 · 16 comments

Comments

@eronakoto
Copy link

String new_text = text.substring(0, text.length - 1);
e_text_editing_controller.value = te_value.copyWith(
text: new_text,
selection: te_value.selection.copyWith(
baseOffset: start,
extentOffset: start
)
);
我写了一个回退删除的函数,但是和我写的EmojiText起了冲突,因为看起来是一个图标,其实actualText的值为:[微笑]
这样就会导致删除的是一个 "]"符号,而没有删除一个EmojiText

我试着去找了一些能够发送键盘指令的代码,但也是一头雾水(不知道如何使用)
BasicMessageChannel keyEvent = BasicMessageChannel(
'flutter/keyevent',
JSONMessageCodec(),
);
keyEvent.send(KeyCodes.BACKSPACE);

@zmtzawqlp
Copy link
Member

final TextEditingValue _value = _textEditingController.value;

@Xiaobai-1024
Copy link

final TextEditingValue _value = _textEditingController.value;

用这个方法删除emoji,控件会报错

@lifelikejuly
Copy link

final TextEditingValue _value = _textEditingController.value;

这个方法对于删除emoji也是有问题的,如果字符串全部都是emoji的话需要删除两次才能正确展示删除后的字符

@zmtzawqlp
Copy link
Member

final TextEditingValue _value = _textEditingController.value;

这个方法对于删除emoji也是有问题的,如果字符串全部都是emoji的话需要删除两次才能正确展示删除后的字符

demo 会出现这个问题吗?

@lifelikejuly
Copy link

lifelikejuly commented Jul 31, 2023

final TextEditingValue _value = _textEditingController.value;

这个方法对于删除emoji也是有问题的,如果字符串全部都是emoji的话需要删除两次才能正确展示删除后的字符

demo 会出现这个问题吗?

是的
输入内容文本:🐶🐰🐼🦁🐸🙉
错误信息如下

 ======== Exception caught by painting library ======================================================
The following ArgumentError was thrown while building a TextSpan:
Invalid argument(s): string is not well-formed UTF-16

When the exception was thrown, this was the stack: 
#0      ParagraphBuilder.addText (dart:ui/text.dart:3130:7)
#1      TextSpan.build (package:flutter/src/painting/text_span.dart:280:17)
#2      TextSpan.build (package:flutter/src/painting/text_span.dart:294:15)
#3      TextPainter._createParagraph (package:flutter/src/painting/text_painter.dart:895:10)
#4      TextPainter.layout (package:flutter/src/painting/text_painter.dart:946:7)
#5      _RenderEditable._layoutText (package:extended_text_field/src/official/rendering/editable.dart:2362:18)
#6      _RenderEditable._computeTextMetricsIfNeeded (package:extended_text_field/src/official/rendering/editable.dart:2390:5)
#7      _RenderEditable.performLayout (package:extended_text_field/src/official/rendering/editable.dart:2476:5)
#8      RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#9      RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#10     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#11     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#12     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#13     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#14     RenderLeaderLayer.performLayout (package:flutter/src/rendering/proxy_box.dart:4724:11)
#15     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#16     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#17     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#18     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#19     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#20     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#21     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#22     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#23     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#24     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#25     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#26     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#27     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#28     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#29     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#30     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#31     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#32     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#33     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#34     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#35     RenderTapRegion.layout (package:flutter/src/widgets/tap_region.dart:519:11)
#36     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#37     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#38     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#39     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#40     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#41     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#42     _RenderDecoration._layoutLineBox (package:flutter/src/material/input_decorator.dart:909:9)
#43     _RenderDecoration._layout (package:flutter/src/material/input_decorator.dart:1031:28)
#44     _RenderDecoration.performLayout (package:flutter/src/material/input_decorator.dart:1350:44)
#45     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#46     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#47     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#48     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#49     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#50     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#51     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#52     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#53     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#54     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#55     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#56     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#57     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#58     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#59     RenderTapRegion.layout (package:flutter/src/widgets/tap_region.dart:519:11)
#60     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:122:14)
#61     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#62     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#63     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:52:11)
#64     RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:808:43)
#65     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:903:32)
#66     RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
#67     RenderBox.layout (package:flutter/src/rendering/box.dart:2386:11)
#68     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:171:12)
#69     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:1056:7)
#70     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:235:7)
#71     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:403:14)
#72     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2234:7)
#73     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1016:18)
#74     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:492:19)
#75     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:905:13)
#76     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:358:5)
#77     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1284:15)
#78     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1214:9)
#79     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1072:5)
#80     _invoke (dart:ui/hooks.dart:142:13)
#81     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:359:5)
#82     _drawFrame (dart:ui/hooks.dart:112:31)
====================================================================================================

@lifelikejuly
Copy link

final TextEditingValue _value = _textEditingController.value;

这个方法对于删除emoji也是有问题的,如果字符串全部都是emoji的话需要删除两次才能正确展示删除后的字符

demo 会出现这个问题吗?

video: https://www.youtube.com/shorts/Iu8E3BhSzS8

@zmtzawqlp
Copy link
Member

final TextEditingValue _value = _textEditingController.value;

这个方法对于删除emoji也是有问题的,如果字符串全部都是emoji的话需要删除两次才能正确展示删除后的字符

demo 会出现这个问题吗?

video: https://www.youtube.com/shorts/Iu8E3BhSzS8

你这个是emoji 呀。。不是你自己定义的表情文字。。也用不上这个实现。

@zmtzawqlp
Copy link
Member

  1. 首先,issue 的作者,是因为 自定义的表情文字。没有设置deleteall,才会键盘默认删除不能删掉整个
  2. emoji 不需要用代码去删除

@lifelikejuly
Copy link

  1. 首先,issue 的作者,是因为 自定义的表情文字。没有设置deleteall,才会键盘默认删除不能删掉整个
  2. emoji 不需要用代码去删除

实际使用场景是无键盘情况下想实现一个删除键,发现删除emoji就不适用了

@zmtzawqlp
Copy link
Member

  1. 首先,issue 的作者,是因为 自定义的表情文字。没有设置deleteall,才会键盘默认删除不能删掉整个
  2. emoji 不需要用代码去删除

实际使用场景是无键盘情况下想实现一个删除键,发现删除emoji就不适用了

emoji 是 特殊的字符,请参考 dart-lang/sdk#35798 自行处理

@zmtzawqlp
Copy link
Member

我在 extended_text 里面也处理过这个问题 fluttercandies/extended_text@c4c4969

@lifelikejuly
Copy link

我在 extended_text 里面也处理过这个问题 fluttercandies/extended_text@c4c4969

😎

@lifelikejuly
Copy link

lifelikejuly commented Jul 31, 2023

我在 extended_text 里面也处理过这个问题 fluttercandies/extended_text@c4c4969

原本我想的是通过参考Runes:last方法来处理

  int get last {
    if (string.length == 0) {
      throw StateError('No elements.');
    }
    int length = string.length;
    int code = string.codeUnitAt(length - 1);
    if (_isTrailSurrogate(code) && string.length > 1) {
      int previousCode = string.codeUnitAt(length - 2);
      if (_isLeadSurrogate(previousCode)) { // 判断是否是UTF-16
        return _combineSurrogatePair(previousCode, code);
      }
    }
    return code;
  }

正常删除emoji代码


  // Is then code (a 16-bit unsigned integer) a UTF-16 lead surrogate.
  bool _isLeadSurrogate(int code) => (code & 0xFC00) == 0xD800;

  // Is then code (a 16-bit unsigned integer) a UTF-16 trail surrogate.
  bool _isTrailSurrogate(int code) => (code & 0xFC00) == 0xDC00;


  int checkCode(String content, [int index = 0]) {
    int code = content.codeUnitAt(index);
    if (_isTrailSurrogate(code) && content.length > 1 && index >= 1) {
      int previousCode = content.codeUnitAt(index - 1);
      if (_isLeadSurrogate(previousCode)) {
        return 2;
      }
    }
    return 1;
  }

  void manualDelete() {
    //delete by code
    final TextEditingValue _value = _textEditingController.value;
    final TextSelection selection = _value.selection;
    if (!selection.isValid) {
      return;
    }

    TextEditingValue value;
    final String actualText = _value.text;
    if (selection.isCollapsed && selection.start == 0) {
      return;
    }
    final int start =
    selection.isCollapsed ? selection.start - 1 : selection.start;
    final int end = selection.end;
    if(checkCode(actualText,end - 1) > 1){
      start --;
    }
    value = TextEditingValue(
      text: actualText.replaceRange(start, end, ''),
      selection: TextSelection.collapsed(offset: start),
    );

    final TextSpan oldTextSpan = _mySpecialTextSpanBuilder.build(_value.text);

    value = handleSpecialTextSpanDelete(value, _value, oldTextSpan, null);

    _textEditingController.value = value;
  }

@zmtzawqlp
Copy link
Member

我在 extended_text 里面也处理过这个问题 fluttercandies/extended_text@c4c4969

原本我想的是通过参考Runes:last方法来处理

  int get last {
    if (string.length == 0) {
      throw StateError('No elements.');
    }
    int length = string.length;
    int code = string.codeUnitAt(length - 1);
    if (_isTrailSurrogate(code) && string.length > 1) {
      int previousCode = string.codeUnitAt(length - 2);
      if (_isLeadSurrogate(previousCode)) { // 判断是否是UTF-16
        return _combineSurrogatePair(previousCode, code);
      }
    }
    return code;
  }

正常删除emoji代码


  // Is then code (a 16-bit unsigned integer) a UTF-16 lead surrogate.
  bool _isLeadSurrogate(int code) => (code & 0xFC00) == 0xD800;

  // Is then code (a 16-bit unsigned integer) a UTF-16 trail surrogate.
  bool _isTrailSurrogate(int code) => (code & 0xFC00) == 0xDC00;


  int checkCode(String content, [int index = 0]) {
    int code = content.codeUnitAt(index);
    if (_isTrailSurrogate(code) && content.length > 1 && index >= 1) {
      int previousCode = content.codeUnitAt(index - 1);
      if (_isLeadSurrogate(previousCode)) {
        return 2;
      }
    }
    return 1;
  }

  void manualDelete() {
    //delete by code
    final TextEditingValue _value = _textEditingController.value;
    final TextSelection selection = _value.selection;
    if (!selection.isValid) {
      return;
    }

    TextEditingValue value;
    final String actualText = _value.text;
    if (selection.isCollapsed && selection.start == 0) {
      return;
    }
    final int start =
    selection.isCollapsed ? selection.start - 1 : selection.start;
    final int end = selection.end;
    if(checkCode(actualText,end - 1) > 1){
      start --;
    }
    value = TextEditingValue(
      text: actualText.replaceRange(start, end, ''),
      selection: TextSelection.collapsed(offset: start),
    );

    final TextSpan oldTextSpan = _mySpecialTextSpanBuilder.build(_value.text);

    value = handleSpecialTextSpanDelete(value, _value, oldTextSpan, null);

    _textEditingController.value = value;
  }

空了可以pr下

@eronakoto
Copy link
Author

eronakoto commented Jul 31, 2023

@zmtzawqlp @Xiaobai-1024 @lifelikejuly
各位实在对不住搞忘来结贴了,这个问题我之前已经解决了。我也用了一些不靠谱的方式来解决。就是没有使用继承复写。
1.
自定义了 Class ExtendedEditableText 的
///build your ccustom text span
final CXSpecialTextSpanBuilder specialTextSpanBuilder;

2.自定义

import 'package:extended_text/extended_text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cim/plugins/ExtendText/EmojiText.dart';
import 'package:flutter_cim/plugins/ExtendText/ImageText.dart';

class CXSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  CXSpecialTextSpanBuilder({this.showAtBackground = false});

  /// whether show background for @somebody
  final bool showAtBackground;
  @override
  TextSpan build(String data, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap}) {
    if (kIsWeb || data == null || data == '') {
      return TextSpan(text: data, style: textStyle);
    }
    /*---*/
    final List<InlineSpan> inlineList = <InlineSpan>[];
    if (data.isNotEmpty) {
      SpecialText? specialText;
      String textStack = '';
      //String text
      for (int i = 0; i < data.length; i++) {
        final String char = data[i];
        textStack += char;
        if (specialText != null) {
          if (!specialText.isEnd(textStack)) {
            specialText.appendContent(char);
          } else {
            inlineList.add(specialText.finishText());
            specialText = null;
            textStack = '';
          }
        } else {
          specialText = createSpecialText(textStack, textStyle: textStyle!, onTap: onTap!, index: i, data: data);
          if (specialText != null) {
            if (textStack.length - specialText.startFlag.length >= 0) {
              textStack = textStack.substring(0, textStack.length - specialText.startFlag.length);
              if (textStack.isNotEmpty) {
                inlineList.add(TextSpan(text: textStack, style: textStyle));
              }
            }
            textStack = '';
          }
        }
      }

      if (specialText != null) {
        inlineList.add(TextSpan(
            text: specialText.startFlag + specialText.getContent(),
            style: textStyle
        ));
      } else if (textStack.isNotEmpty) {
        inlineList.add(TextSpan(text: textStack, style: textStyle));
      }
    } else {
      inlineList.add(TextSpan(text: data, style: textStyle));
    }

    return TextSpan(children: inlineList, style: textStyle);
    return super.build(data, textStyle: textStyle, onTap: onTap);
  }

  @override
  SpecialText? createSpecialText(String flag, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, int? index, String? data}) {
    SpecialText? st;
    if (flag == null || flag == '') {
      return st;
    }
    ///index is end index of start flag, so text start index should be index-(flag.length-1)
    if (isStart(flag, EmojiText.flag)) {
      EmojiText et = EmojiText(textStyle!, start: index! - (EmojiText.flag.length - 1), data: data!);
      if (et.validKey()) {
        return et;
      }
    } else if (isStart(flag, ImageText.flag)) {
      return ImageText(textStyle!, start: index! - (ImageText.flag.length - 1), onTap: onTap!);
    }
    return st;
  }
}
`

3.如果要使用函数触发删除,正如官方demo一样
`
void eManualDelete() {
    //delete by code
    final TextEditingValue _value = e_text_editing_controller!.value;
    final TextSelection selection = _value.selection;
    if (!selection.isValid) {
      return;
    }

    TextEditingValue value;
    final String actualText = _value.text;
    if (selection.isCollapsed && selection.start == 0) {
      return;
    }
    final int start = selection.isCollapsed ? selection.start - 1 : selection.start;
    final int end = selection.end;

    value = TextEditingValue(
      text: actualText.replaceRange(start, end, ''),
      selection: TextSelection.collapsed(offset: start),
    );

    final TextSpan oldTextSpan = cx_special_text_span_builder!.build(_value.text);

    value = handleSpecialTextSpanDelete(value, _value, oldTextSpan, null);

    e_text_editing_controller!.value = value;
    bottom_tool!.val_change(value.text);
  }

@zmtzawqlp
Copy link
Member

@zmtzawqlp @Xiaobai-1024 @lifelikejuly 各位实在对不住搞忘来结贴了,这个问题我之前已经解决了。我也用了一些不靠谱的方式来解决。就是没有使用继承复写。 1. 自定义了 Class ExtendedEditableText 的 ///build your ccustom text span final CXSpecialTextSpanBuilder specialTextSpanBuilder;

2.自定义

import 'package:extended_text/extended_text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cim/plugins/ExtendText/EmojiText.dart';
import 'package:flutter_cim/plugins/ExtendText/ImageText.dart';

class CXSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  CXSpecialTextSpanBuilder({this.showAtBackground = false});

  /// whether show background for @somebody
  final bool showAtBackground;
  @override
  TextSpan build(String data, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap}) {
    if (kIsWeb || data == null || data == '') {
      return TextSpan(text: data, style: textStyle);
    }
    /*---*/
    final List<InlineSpan> inlineList = <InlineSpan>[];
    if (data.isNotEmpty) {
      SpecialText? specialText;
      String textStack = '';
      //String text
      for (int i = 0; i < data.length; i++) {
        final String char = data[i];
        textStack += char;
        if (specialText != null) {
          if (!specialText.isEnd(textStack)) {
            specialText.appendContent(char);
          } else {
            inlineList.add(specialText.finishText());
            specialText = null;
            textStack = '';
          }
        } else {
          specialText = createSpecialText(textStack, textStyle: textStyle!, onTap: onTap!, index: i, data: data);
          if (specialText != null) {
            if (textStack.length - specialText.startFlag.length >= 0) {
              textStack = textStack.substring(0, textStack.length - specialText.startFlag.length);
              if (textStack.isNotEmpty) {
                inlineList.add(TextSpan(text: textStack, style: textStyle));
              }
            }
            textStack = '';
          }
        }
      }

      if (specialText != null) {
        inlineList.add(TextSpan(
            text: specialText.startFlag + specialText.getContent(),
            style: textStyle
        ));
      } else if (textStack.isNotEmpty) {
        inlineList.add(TextSpan(text: textStack, style: textStyle));
      }
    } else {
      inlineList.add(TextSpan(text: data, style: textStyle));
    }

    return TextSpan(children: inlineList, style: textStyle);
    return super.build(data, textStyle: textStyle, onTap: onTap);
  }

  @override
  SpecialText? createSpecialText(String flag, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, int? index, String? data}) {
    SpecialText? st;
    if (flag == null || flag == '') {
      return st;
    }
    ///index is end index of start flag, so text start index should be index-(flag.length-1)
    if (isStart(flag, EmojiText.flag)) {
      EmojiText et = EmojiText(textStyle!, start: index! - (EmojiText.flag.length - 1), data: data!);
      if (et.validKey()) {
        return et;
      }
    } else if (isStart(flag, ImageText.flag)) {
      return ImageText(textStyle!, start: index! - (ImageText.flag.length - 1), onTap: onTap!);
    }
    return st;
  }
}
`

3.如果要使用函数触发删除,正如官方demo一样
`
void eManualDelete() {
    //delete by code
    final TextEditingValue _value = e_text_editing_controller!.value;
    final TextSelection selection = _value.selection;
    if (!selection.isValid) {
      return;
    }

    TextEditingValue value;
    final String actualText = _value.text;
    if (selection.isCollapsed && selection.start == 0) {
      return;
    }
    final int start = selection.isCollapsed ? selection.start - 1 : selection.start;
    final int end = selection.end;

    value = TextEditingValue(
      text: actualText.replaceRange(start, end, ''),
      selection: TextSelection.collapsed(offset: start),
    );

    final TextSpan oldTextSpan = cx_special_text_span_builder!.build(_value.text);

    value = handleSpecialTextSpanDelete(value, _value, oldTextSpan, null);

    e_text_editing_controller!.value = value;
    bottom_tool!.val_change(value.text);
  }

你的问题应该是没有给 emoji text 加deleteall 等于 true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants