0

I'm a dart noob, and would like to make a keyboard with symbols and hieroglyphs in the keyboard, so this is my attempt! but when I use the backspace it freaks out: project is open-source on github: https://github.com/bookla-foundation/hieroglyphs error:

For more information see https://dart.dev/null-safety/unsound-null-safety
I/flutter ( 4728): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter ( 4728): The following ArgumentError was thrown during performLayout():
I/flutter ( 4728): Invalid argument(s): string is not well-formed UTF-16
I/flutter ( 4728):
I/flutter ( 4728): The relevant error-causing widget was:
I/flutter ( 4728):   TextField file:///Users/xxx/workspace/dart/heeroghleefy/lib/main.dart:30:11
I/flutter ( 4728):
I/flutter ( 4728): When the exception was thrown, this was the stack:
I/flutter ( 4728): #0      ParagraphBuilder.addText (dart:ui/text.dart:2178:7)
I/flutter ( 4728): #1      TextSpan.build (package:flutter/src/painting/text_span.dart:210:15)
I/flutter ( 4728): #2      TextPainter.layout (package:flutter/src/painting/text_painter.dart:569:14)
I/flutter ( 4728): #3      RenderEditable._layoutText (package:flutter/src/rendering/editable.dart:2011:18)
I/flutter ( 4728): #4      RenderEditable.performLayout (package:flutter/src/rendering/editable.dart:2059:5)
I/flutter ( 4728): #5      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #6      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #7      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #8      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #9      RenderLeaderLayer.performLayout (package:flutter/src/rendering/proxy_box.dart:5034:11)
I/flutter ( 4728): #10     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #11     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #12     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #13     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #14     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #15     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #16     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #17     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #18     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #19     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #20     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #21     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #22     RenderCustomPaint.performLayout (package:flutter/src/rendering/custom_paint.dart:518:11)
I/flutter ( 4728): #23     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #24     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #25     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #26     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #27     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #28     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #29     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #30     _RenderDecoration._layoutLineBox (package:flutter/src/material/input_decorator.dart:931:9)
I/flutter ( 4728): #31     _RenderDecoration._layout (package:flutter/src/material/input_decorator.dart:1033:28)
I/flutter ( 4728): #32     _RenderDecoration.performLayout (package:flutter/src/material/input_decorator.dart:1294:44)
I/flutter ( 4728): #33     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #34     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #35     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #36     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #37     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #38     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #39     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #40     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:123:14)
I/flutter ( 4728): #41     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #42     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:54:11)
I/flutter ( 4728): #43     RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:830:43)
I/flutter ( 4728): #44     RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:932:32)
I/flutter ( 4728): #45     RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
I/flutter ( 4728): #46     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:171:12)
I/flutter ( 4728): #47     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:912:7)
I/flutter ( 4728): #48     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:243:7)
I/flutter ( 4728): #49     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:407:14)
I/flutter ( 4728): #50     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1634:7)
I/flutter ( 4728): #51     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:884:18)
I/flutter ( 4728): #52     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:455:19)
I/flutter ( 4728): #53     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:903:13)
I/flutter ( 4728): #54     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1119:15)
I/flutter ( 4728): #55     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1057:9)
I/flutter ( 4728): #59     _invoke (dart:ui/hooks.dart:157:10)
I/flutter ( 4728): #60     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:253:5)
I/flutter ( 4728): #61     _drawFrame (dart:ui/hooks.dart:120:31)
I/flutter ( 4728): (elided 3 frames from dart:async)
I/flutter ( 4728):
I/flutter ( 4728): The following RenderObject was being processed when the exception was fired: RenderEditable#d149e relayoutBoundary=up18 NEEDS-LAYOUT NEEDS-PAINT:
I/flutter ( 4728):   creator: _Editable-[GlobalKey#5bfdd] ← Semantics ← CompositedTransformTarget ←
I/flutter ( 4728):     IgnorePointer-[GlobalKey#826e3] ← Semantics ← Listener ←
I/flutter ( 4728):     RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#e8b2c] ← Listener ← _ScrollableScope
I/flutter ( 4728):     ← RepaintBoundary ← CustomPaint ← RepaintBoundary ← ⋯
I/flutter ( 4728):   parentData: <none> (can use size)
I/flutter ( 4728):   constraints: BoxConstraints(w=387.4, 0.0<=h<=Infinity)
I/flutter ( 4728):   size: Size(387.4, 32.0)
I/flutter ( 4728):   cursorColor: Color(0xff2196f3)
I/flutter ( 4728):   showCursor: ValueNotifier<bool>#d5002(true)
I/flutter ( 4728):   maxLines: 1
I/flutter ( 4728):   minLines: null
I/flutter ( 4728):   selectionColor: Color(0x662196f3)
I/flutter ( 4728):   textScaleFactor: 1.0
I/flutter ( 4728):   locale: en_US
I/flutter ( 4728):   selection: TextSelection(baseOffset: 5, extentOffset: 5, affinity: TextAffinity.downstream,
I/flutter ( 4728):     isDirectional: false)
I/flutter ( 4728):   offset: ScrollPositionWithSingleContext#51e24(offset: 0.0, range: 0.0..0.0, viewport: 387.4,
I/flutter ( 4728):     ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#4e8d9,
I/flutter ( 4728):     ScrollDirection.idle)
I/flutter ( 4728): This RenderObject had the following child:
I/flutter ( 4728):     text: TextSpan
I/flutter ( 4728): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/chatty  ( 4728): uid=10135(com.example.heeroghleefy) 1.ui identical 2 lines
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/flutter ( 4728): Another exception was thrown: Null check operator used on a null value
I/flutter ( 4728): Another exception was thrown: Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.
Omar
  • 8,374
  • 8
  • 39
  • 50

1 Answers1

1

The question and this answer is related to an issue with the original version of the article Custom In-App Keyboard in Flutter. The article has been updated now so backspacing should no longer cause a crash.

The problem was caused by trying to only delete half of a surrogate pair. The way to fix it to check for surrogates before deleting:

void _backspace() {
  final text = _controller.text;
  final textSelection = _controller.selection;
  final selectionLength = textSelection.end - textSelection.start;

  // There is a selection.
  if (selectionLength > 0) {
    final newText = text.replaceRange(
      textSelection.start,
      textSelection.end,
      '',
    );
    _controller.text = newText;
    _controller.selection = textSelection.copyWith(
      baseOffset: textSelection.start,
      extentOffset: textSelection.start,
    );
    return;
  }

  // The cursor is at the beginning.
  if (textSelection.start == 0) {
    return;
  }

  // Delete the previous character
  final previousCodeUnit = text.codeUnitAt(textSelection.start - 1);
  final offset = _isUtf16Surrogate(previousCodeUnit) ? 2 : 1;
  final newStart = textSelection.start - offset;
  final newEnd = textSelection.start;
  final newText = text.replaceRange(
    newStart,
    newEnd,
    '',
  );
  _controller.text = newText;
  _controller.selection = textSelection.copyWith(
    baseOffset: newStart,
    extentOffset: newStart,
  );
}

bool _isUtf16Surrogate(int value) {
  return value & 0xF800 == 0xD800;
}

The _controller refers to a TextEditingController for a TextField.

See this related question regarding deleting grapheme clusters larger than a single surrogate pair:

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393