how to set cursor position at the end of the value in flutter in textfield?
Asked Answered
T

22

168

I want to add ,00 at the end of the value of the textfield.

It works well in iOS device, but in android cursor moves to starting point of the value in textfield. As a result, I'm unable to add ,00.

Trihedral answered 2/7, 2019 at 11:44 Comment(1)
Please post some code snippet, What you have tried so far.Eroto
R
387

I've been having the same problem with setting a TextEditingController and this is what worked for me.

controller.text = someString;
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));

TextSelection.fromPosition() does the following (from the documentation):

Creates a collapsed selection at the given text position. A collapsed selection starts and ends at the same offset, which means it contains zero characters but instead serves as an insertion point in the text.

Edit - another version, which is a bit shorter and suggested by others here:

controller.text = someString;
controller.selection =
          TextSelection.collapsed(offset: controller.text.length);
Recollect answered 30/12, 2019 at 4:17 Comment(6)
You saved me !! many thanks. I actually only need to do this with Material Textfield , CupertinoTextfield behaves as expected.. when assign a manipulated text to its TextEditingController cursor is always at the end. As I thing that this should be the default behavior for a textfield I filed an Issue to the Flutter team as this kinda kills Flutter purpose if you have different behaviors like this.. it ain't really multi platform.. I'm sure that this is due to different teams.. but hey, the should agree on things.. Anyways.. thanks again for the elegant workaround.Carlenecarleton
Hi. After I upgraded to Catalina, Android Studio 4.0 Flutter latest dev channel the code from your solution is not working anymore. Do you know about any changes that invalidate this solution? Here you can find the code I'm using in my app #62244527. I actually found out that this solution is only needed on web.. on device ( only tested on iPad ) new text gets just added at the end..as expected.Carlenecarleton
I forgot.. here is a sample app I put on GitHub to show the problem to the flutter team github.com/vinnytwice/material_textfield_issue_test ;) Cheers.Carlenecarleton
ok.. checked on iPad and your code is also needed on iOS after upgrading to Catalina/Android Studio 4.0, Xcode 11.5.. well.. at least now all platforms are aligned .. lolCarlenecarleton
This doesn't work when keyboard has dictionary, autocorrect and/or suggestionsCahn
That's interesting, can you provide a code sample? I'm using this solution with overlay popup suggestions and I'm wondering how your situation is different.Recollect
S
34

This worked for me:

_inputCtrl.value = TextEditingValue(
  text: suggestion,
  selection: TextSelection.collapsed(offset: suggestion.length),
);

https://github.com/flutter/flutter/issues/11416#issuecomment-507040665

Succursal answered 18/7, 2020 at 19:54 Comment(4)
Only this solution worked for me. The other ones had timing issues because setting the text with editingController.text = 'something'; took some milliseconds and therefore the subsequent editingController.selection = TextSelection.fromPosition(...); wouldn't have any effect.Broom
That's the only solution worked for me. I guess there's a sync issue with the previous answers, giving the text and the selection at the same like this works like a charm.Tye
This should be the accepted answer tbhLandtag
This code can also be placed in a TextInputFormatter that is installed on the TextField with inputFormatters.Saprogenic
V
22

I solved my issue by using this

this will set your cursor at the end in the textfield.

you can call the code from wherever and whenever it will set the cursor location when it is called.

or simple place this code in onChanged() method of textfield

final val = TextSelection.collapsed(offset: _textTEC.text.length); 
 _textTEC.selection = val;
Volt answered 13/10, 2019 at 17:0 Comment(0)
P
20

You can use .. operator If you are setting value in the controller as below:

final _controller = TextEditingController()..text = txtValue
      ..selection = TextSelection.collapsed(offset: txtValue.length);

)

I think it's very useful when you are setting value runtime.

Pathy answered 19/7, 2021 at 11:12 Comment(2)
thank you. This seems to be the clearest and cleanest way.Auxesis
it is useful when you set value at run timeResolve
T
13

Simple and easy solution to move cursor at the end of position after updating the text in textfield just add below line.

textController.selection =
      TextSelection.collapsed(offset: textController.text.length);
Tired answered 4/9, 2022 at 16:43 Comment(0)
F
10

This works for me like a charm.

myController.text = newText;
myController.selection = TextSelection(
    baseOffset: newText.length,
    extentOffset: newText.length)
Friedman answered 21/1, 2021 at 0:41 Comment(0)
N
7

The solution that seems to work is the following:

  1. Give a TextEditingController argument to the constructor of your text field:

    var myTextEditingController = TextEditingController();
    var myTextField = TextField(..., controller: myTextEditingController);
    
  2. When setting the new text inside your text field, use the TextEditingController like that:

    myTextEditingController.value.copyWith(
      text: newText,
      selection: TextSelection(
        baseOffset: newText.length,
        extentOffset: newText.length
      )
    )
    

This seems extraordinary complicated for what it achieves but it seems to be the only solution to the problem of the cursor not being updated in Flutter text fields.

Norvil answered 10/7, 2019 at 18:17 Comment(1)
Why do you need copyWith?Tupungato
R
6

Checked the notes of the setter of TextEditingController's text, it says the [text] and the [selection] shouldn't be changed at the same time, however for the phone inputting, we'd like to apply some format for the input (e.g. '131 1111 1111') while keeping the cursor always at the end .

So I just use a customized TextEditingController, it works!

class PhoneEditingController extends TextEditingController {

  @override
  set text(String newText) {
    value = value.copyWith(
    text: newText,
    selection: TextSelection.collapsed(offset: newText.length),
    composing: TextRange.empty,
  );

}
Rociorock answered 28/2, 2020 at 12:43 Comment(2)
Maybe they've updated it? I read it as encouraging to update both at the same time instead of separately 🤔/// This property can be set from a listener added to this [TextEditingController]; however, one should not also set [selection] in a separate statement. To change both the [text] and the [selection] change the controller's [value].Junkie
Pal I would give you two likes if stack overflow let me. In my case, using a phone input formatter (that is, one that adds hyphens, parenthesis, etc. when writing) it is your answer that works. Thanks.Sigmon
A
6

In case your new value is too long. You should scroll the view to the new cursor position.

  1. Add TextEditingController and ScrollController to TextField.

Remember to init/dispose them in initState and dispose

TextField(
controller: controller,
scrollController: scrollController,
)
  1. Set a new value and cursor position for the TextEditingController
controller.text = newValue;
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
  1. Scroll the view to the position
Future.delayed(Duration(milliseconds: 50),(){
 scrollController.jumpTo(scrollCtrl.position.maxScrollExtent);
});

Ailurophobe answered 6/10, 2021 at 7:28 Comment(1)
Thanks for the great suggestion about scrollController. I never realized that TextField had one until today.Solanum
A
3

the following code worked perfectly for me.

_controller.value = _controller.value.copyWith(
  text: newText,
  selection: TextSelection.fromPosition(
    TextPosition(offset: newText.length),
  ),
);
Announcer answered 28/4, 2020 at 22:18 Comment(0)
I
3

Using _textController.text.length as the cursor's base offset doesn't work if you want to add your special characters (in your case 00) somewhere in the middle of the text. In such cases, the following solution should work:

String specialChars = '00';
int length = specialChars.length;
// current cursor position, where you want to add your special characters
int cursorPos = _textController.selection.base.offset;
// the text before the point you want to add the special characters
String prefixText = _textController.text.substring(0, cursorPos);
// the text after the point ...
String suffixText = _textController.text.substring(cursorPos);

_textController.text = prefixText + specialChars + suffixText;
// set the cursor position right after the special characters
_textController.selection = TextSelection(baseOffset: cursorPos + length, extentOffset: cursorPos + length);

or you can do it like this, it's the same thing:

int cursorPos = _textController.selection.base.offset;
_textController.value = _textController.value.copyWith(
  text: _textController.text.replaceRange(cursorPos, cursorPos, '00'),
  selection: TextSelection.fromPosition(TextPosition(offset: cursorPos + 2))
);
Inapposite answered 6/6, 2020 at 1:58 Comment(0)
D
3

Create a new Dart class, extending TextEditingController and you can use your custom TextController instead that behaves:

class TextController extends TextEditingController {

  TextController({String text}) {
    this.text = text;
  }

  set text(String newText) {
    value = value.copyWith(
      text: newText,
      selection: TextSelection.collapsed(offset: newText.length),
      composing: TextRange.empty
    );
  }
}
Durrace answered 4/11, 2020 at 16:53 Comment(0)
D
3

There are a lot of solutions but in the code below I've merged the best answers from Github and SO.

This below has null-safety for Dart >= 2.12 and uses latest TextSelection APIs

import 'package:flutter/widgets.dart';

class TextEditingControllerWithEndCursor extends TextEditingController {

  TextEditingControllerWithEndCursor({
    String? text
  }): super(text: text);

  @override
  set text(String newText) {
    value = value.copyWith(
      text: newText,
      selection: TextSelection(
        baseOffset: newText.length,
        extentOffset: newText.length
      ),
      composing: TextRange.empty,
    );
  }
}
Decompound answered 9/7, 2021 at 9:43 Comment(1)
On further testing, this solution is not working well so I used @Recollect solutionUpstart
A
2

I have a TextField, and I have a method that appends text to it periodically. I wanted to scroll horizontally to the rightmost end of that text field to see the last appended string, or I want to move the cursor to the last character appended.

The only trick that worked for me was to use a scrollController, and call the jump() method every time I append text:

TextField(
    scrollController: scrollController,
    controller: textFieldController
)

Now jump to the end of the TextField:

scrollController.jumpTo( scrollController.position.pixels+500);
Ashelyashen answered 16/9, 2021 at 2:9 Comment(0)
E
2

Use a StatefulWidget and store a TextEditingController.

class SampleTextFieldextends StatefulWidget {
  const SampleTextField({super.key});

  @override
  State<SampleTextField> createState() => _SampleTextFieldState();
}

class _SampleTextFieldStateextends State<SampleTextField> {
  late final TextEditingController controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      onChanged: (value) {
        controller.text = value;
        controller.selection =
            TextSelection.collapsed(offset: controller.text.length);
      },

      onTap: () {
        controller.selection =
            TextSelection.collapsed(offset: controller.text.length);
      },

      onTapOutside: (event) {
        FocusScope.of(context).unfocus();
      },

    );
  }
}

This answer put curser at the end of the text field in right to left (ltr) languages like Farsi / Persian / Arabic.

Electroshock answered 10/5, 2023 at 12:31 Comment(0)
G
1

I also had a similar problem trying to change the text in the TextEditingController.text into a formatted string matching NumberFormat("###,###,###.##") inside onChanged while trying to preserve the current cursor location and ended up doing something like this:

TextFormField(
  controller: controller,

  onChanged: (value) {
    final formatter = NumberFormat("###,###,###.##");
    final amount = formatter.parse(value);

    //allow user to enter any letter at the end without clearing it.
    //otherwise if the user enters "." it would get truncated by
    //the formatter
    if (amount == 0 || value.startsWith(formatter.format(amount.floor()))) {
      return;
    }

    final editor = controller.value;

    //only do something if the IME is not in the middle of composing
    if (editor.composing.isCollapsed) {
      //offset is the position of the cursor
      int offset = editor.selection.extentOffset;

      final pretty = formatter.format(amount);

      if(offset >= value.length) {
        //set the offset to the length of the new string
        offset = pretty.length;

      } else {
        //count how many chars are in the string to the left
        //of the cursor position, excluding formatting chars [,.-]

        final partial = value.substring(0, offset);
        for (var unit in partial.codeUnits) {
          if (unit >= 44 && unit <= 46) offset--;
        }

        //traverse the formatted string by the same number of
        //characters skipping over the formatting chars [,.-].
        int i = 0;
        for (var unit in pretty.codeUnits) {
          if (i++ >= offset) break;
          if (unit >= 44 && unit <= 46) offset++;
        }
        //offset is now where the new cursor position should be
      }

      //finally update the controller to the new offset
      controller.value = editor.copyWith(
        text: pretty,
        selection: TextSelection.collapsed(offset: offset),
        composing: TextRange.collapsed(offset),
      );
    }
  },
)
Ger answered 7/12, 2021 at 14:32 Comment(0)
S
1

A simple example will help you.

 if (int.parse(mintues) > 59) mintuesController.text = '59';
                mintuesController.selection = TextSelection.fromPosition(
                    TextPosition(offset: mintues.length));
Spectacles answered 26/1, 2022 at 9:1 Comment(1)
There are 16 existing answers to this question, including a top-voted, accepted answer with over two hundred votes. Are you certain your solution hasn't already been given? If not, why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is always useful on Stack Overflow, but it's especially important where the question has been resolved to the satisfaction of both the OP and the community. Help readers out by explaining what your answer does different and when it might be preferred.Manmade
A
1

The easy way is

TextField(
    decoration: InputDecoration(labelText: "Email"),
    controller: TextEditingController.fromValue(TextEditingValue(text: 
    useremail,selection: TextSelection.collapsed(offset: useremail.length))),
    onChanged: (value) => useremail= value,
)
Archduchess answered 19/4, 2023 at 2:7 Comment(0)
L
1

in my case and because I'm using getx as state management, I solved the problem by:

TextField(
 controller: TextEditingController.fromValue(TextEditingValue(
                  text: con.nominator1String.value,
                  selection: TextSelection.collapsed(
                      offset: con.nominator1String.value.length)))
)

con is my getx controller

Lalalalage answered 19/7, 2023 at 10:23 Comment(0)
E
1

To change text cursor after change not just end of the string but also middle of string you can use this snippet on onChange property of TextField:

(newValue){
   int changedPosition =
            determineChangedPosition(previousTextFieldValue, newValue);

   // Set the cursor position at the changed position
    controller.selection = TextSelection.fromPosition(
       TextPosition(offset: newValue.length == changedPosition
                  ? changedPosition
                  : changedPosition + 1)),
    );
}

use StatefullWidget to store previousTextFieldValue and here is determineChangedPosition method:

  int determineChangedPosition(String oldValue, String newValue) {
    final minLength =
        oldValue.length < newValue.length ? oldValue.length : newValue.length;

    for (var i = 0; i < minLength; i++) {
      if (oldValue[i] != newValue[i]) {
        return i;
      }
    }
    return minLength;
  }

Electroshock answered 20/7, 2023 at 6:40 Comment(1)
Please improve and refactor the above algorithm.Electroshock
W
0

You need a FocusNode and set TextSelection to place the cursor.

The code here may be a start: Position cursor at end of TextField when focused?

Woolgathering answered 2/7, 2019 at 11:55 Comment(1)
i tried this one but as mention in your suggestion it's not working.Trihedral
N
0

According to the documentation the possible way to set the text and position together is using value... like this:

_controller.value = _controller.value.copyWith(text: newText, selection: newSelection)
Nicotinism answered 6/3, 2021 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.