admin管理员组

文章数量:1129706

I'm trying to implement a multi-line textfield whose value can be altered via controller. My usecase is that you can press a button and a set of string will be added to the textfield on current cursor position. The problem is my code worked but the textfield viewport doesn't change, it showed its state as before the string was added to the textfield, not scrolling to the current cursor position.

I'd tried using controller(TextEditingController) and scrollController to manually manipulate the TextField's text value and scroll position. The text was inserted to cursor position beautifully, but the calculated scroll position was no use (I used controller.selection.baseOffset but it appears I have to calculate the selection point with textfield's width to find the line the cursor is at ???). Can someone help guiding me how to achieve that?

FYI: this is how I assign value to textfield

  void insertParam(String text, TextSelection selection) {
    final TextEditingValue value = controller.value;
    final int start = selection.baseOffset;
    int end = selection.extentOffset;
    if (selection.isValid) {
      String newText = '';
      if (value.selection.isCollapsed) {
        if (end > 0) {
          newText += value.text.substring(0, end);
        }
        newText += text;
        if (value.text.length > end) {
          newText += value.text.substring(end, value.text.length);
        }
      } else {
        newText = value.text.replaceRange(start, end, text);
        end = start;
      }
      controller.value = value.copyWith(
        text: newText,
        selection: selection.copyWith(
          baseOffset: end + text.length,
          extentOffset: end + text.length,
        ),
      );
    } else {
      controller.value = TextEditingValue(
        text: text,
        selection: TextSelection.fromPosition(TextPosition(offset: text.length)),
      );
    }
  }

I'm trying to implement a multi-line textfield whose value can be altered via controller. My usecase is that you can press a button and a set of string will be added to the textfield on current cursor position. The problem is my code worked but the textfield viewport doesn't change, it showed its state as before the string was added to the textfield, not scrolling to the current cursor position.

I'd tried using controller(TextEditingController) and scrollController to manually manipulate the TextField's text value and scroll position. The text was inserted to cursor position beautifully, but the calculated scroll position was no use (I used controller.selection.baseOffset but it appears I have to calculate the selection point with textfield's width to find the line the cursor is at ???). Can someone help guiding me how to achieve that?

FYI: this is how I assign value to textfield

  void insertParam(String text, TextSelection selection) {
    final TextEditingValue value = controller.value;
    final int start = selection.baseOffset;
    int end = selection.extentOffset;
    if (selection.isValid) {
      String newText = '';
      if (value.selection.isCollapsed) {
        if (end > 0) {
          newText += value.text.substring(0, end);
        }
        newText += text;
        if (value.text.length > end) {
          newText += value.text.substring(end, value.text.length);
        }
      } else {
        newText = value.text.replaceRange(start, end, text);
        end = start;
      }
      controller.value = value.copyWith(
        text: newText,
        selection: selection.copyWith(
          baseOffset: end + text.length,
          extentOffset: end + text.length,
        ),
      );
    } else {
      controller.value = TextEditingValue(
        text: text,
        selection: TextSelection.fromPosition(TextPosition(offset: text.length)),
      );
    }
  }
Share Improve this question edited Jan 8 at 8:51 fanggiz asked Jan 8 at 8:47 fanggizfanggiz 114 bronze badges New contributor fanggiz is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 10
  • check stackoverflow.com/questions/77890325/… how RenderEditable.getBoxesForSelection() is used – pskink Commented Jan 8 at 8:57
  • Ok I'm testing it out, thanks for the info – fanggiz Commented Jan 8 at 9:06
  • all you need is to get y position of the selection box and pass that position to ScrollController.animateTo / jumpTo method – pskink Commented Jan 8 at 9:08
  • Ok after reading your mentioned thread I'm a bit confused. The thread said to wrap the textfield with CustomPaint() and get the renderEditable from paint() function. Do I have to do the same, as the RenderEditable.getBoxedForSelection() is not static function – fanggiz Commented Jan 8 at 9:41
  • Oh... as I'm reading the code I saw something. Let me test it out again – fanggiz Commented Jan 8 at 9:44
 |  Show 5 more comments

1 Answer 1

Reset to default 1

Ok after talking to @pskink comment I reached the solution

void initState() {
    super.initState();

  // other code

  SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
    context.findRenderObject()?.visitChildren(_visitor);
  });
}

void _visitor(RenderObject child) {
    if (child is RenderEditable) {
      setState(() {
        // assign RenderEditable node to widget state
        // make sure you get the correct child, for me there is only one textfield for testing
        reEdt = child;
      });
      return;
    }
    child.visitChildren(_visitor);
  }


// call when inserting text and want to scroll to cursor
void scrollToSelection(TextSelection selection) {
    // find local rect of cursor or starting selection in case of selecting text 
    final localRect = reEdt?.getLocalRectForCaret(TextPosition(offset: selection.baseOffset));
    if (localRect == null) return;
    scrollController.jumpTo(localRect.top);
  }

and don't forget to assign scrollController to TextField

本文标签: