I have a textfield controller in Flutter and wish to return the position which the cursor occupies in the textfield?
Keep in mind a user may have typed in text then moved the cursor back along the text to say edit the text.
TextField(
onChanged: (value) {
//TODO - Return Cursor Position
}
),
the cursor posistion in text field
first make controller for text field now
TextField(
onChanged: (value) {
int cursorPos = _textController.selection.base.offset;
print(cursorPos);
//TODO - Return Cursor Position
}
),
Related
I want to create a view with a TextField whose default behavior is disabled (i.e. I don't enable the default keyboard on click).
I created my own keyboard because it allows me to do specific actions for my application.
The keyboard works well and populates the TextField.
My problem
I can't manage the blinking cursor in the TextField. Indeed when I type on my keyboard the cursor follows my text so no problem.
On the other hand, if I decide to click manually in the middle of my text, the cursor moves but the new characters entered do not go to the location of the cursor.
How to do this ?
What does it look like ?
My code
My TextField Widget:
InputAtom(
autofocus: true,
controller: controllerInput,
placeholder: "Placeholder",
fontSize: 35,
keyboard: TextInputType.none,
enableInteractiveSelection: true,
showCursor: true,
cursorColor: Colors.red,
enableSuggestions: false,
autocorrect: false,
),
My button keyboard example:
KeyboardButtonAtom.numeric({
Key? key,
required String text,
required TextEditingController controller,
}): super(
key: key,
text: text,
controller: controller,
onPressed: (){
// Here I add the number typed on the keyboard after
controller.text += text.toString();
// Here the cursor moves to the end of my controller.text
controller.selection = TextSelection.collapsed(offset: controller.text.length);
print(controller.selection.baseOffset);
}
);
How to retrieve the cursor position when I type somewhere by hand in my TextField to be able to add my numbers from the cursor position?
EDIT
In my KeyboardButtonAtom, I do that and it works. Thanks to #Kaushik Chandru
Here is the code that works regardless of the position of the cursor even placed by hand in the middle of your character string
String textBeforeCursor = controller.text.substring(0, controller.selection.baseOffset);
String textAfterCursor = controller.text.substring(controller.selection.extentOffset);
controller.text = textBeforeCursor + text.toString() + textAfterCursor;
int cursorPosition = textBeforeCursor.length + 1;
controller.selection = TextSelection.collapsed(offset: cursorPosition);
To get the current position of the cursor you can try
var cursorPos = _textEditController.selection.base.offset;
String textAfterCursor = _textEditController.text.substring(cursorPos);
String textBeforeCursor = _textEditController.text.substring(0, cursorPos);
_textEditController.text = textBeforeCursor + "someText" + textAfterCursor;
_textEditingController.selection = TextSelection.collapsed(offset: _textEditingController.text.length);
like i have an edit profile page and inside text form field i populate textform field with existing data but when i press on that textform field i want my cursor to start from the last index of my pre loaded data but the keyboard cursor moves to starting point is there a way to achieve this.
this is my text form field
TextFormField(
decoration:
const InputDecoration(hintText: "First Name"),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter your first name";
} else {
return null;
}
},
onChanged: (value) {
firstName = firstNameController.text;
if (firstName.isNotEmpty) {
firstName =
"${firstName[0].toUpperCase()}${firstName.substring(1)}";
firstNameController.selection =
TextSelection.fromPosition(
TextPosition(offset: firstName.length));
}
},
controller: firstNameController..text = firstName,
),
i am able to populate data inside textform field but the problem is when i press on textformfield i want my keyboard starting point to be at the last index of this populated data but keyboard cursor moves to the starting point no matter what need some help here thanks.
You can use selection like this
firstNameController.selection = TextSelection.fromPosition(
TextPosition(
offset: firstNameController.text.length));
Update: this problem may be because of setting the TextField's 'readOnly' attribute to true. If so, I still need to make sure the keyboard does not pop up, since I set readOnly to true to disable the keyboard.
BACKGROUND
My app adds letters and newlines from a custom button widgets to a display that has a TextField widget. I do not use a keyboard for this. I use the Provider package and a ChangeNotifier called AppBrain to manage state. AppBrain has the text information and edits it when a letter is selected from the selection bar.
The textfield is a scrollable widget when there are too many lines to fit in its dimensions. But whenever I add letters and a new line of text is created, the textfield does not scroll down and the cursor and edited line is obscured.
I would like to know an easy way to scroll until the cursor is visible again. (When the cursor is at the end of the text, I can scroll to the bottom. When it is in the middle of the text, I just need to scroll it one line down.)
POSSIBLE FIXES
When I reenable the keyboard to edit the TextField, it does it automatically. If anyone knows how the keyboard edits the TextField, I might be able to add that to my addWord function.
I tried using a ScrollController to scroll the TextField using its animateTo() function. The problem I have is that I usually need to just scroll down 1 line, but I don't have the exact pixel height of my lines of text. I also don't know when a line wraps to a new line, which would require a scroll down.
Perhaps if I had the position of my cursor, I could use it along with my TextField's dimensions to scroll down accordingly.
Picture of my app
CODE
Textfield widget in Text Display
Portion of code in Text Display
...
Positioned(//Text Display
top:0,
left:0,
width:kScreenDim.dx,
height:290,
child: Container(
color: kAppBarBackgroundColor,
child: Container(
padding: EdgeInsets.only(left: kTextMargin),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft:Radius.elliptical(17,20),
topRight: Radius.elliptical(17,20)
),
color:kTextDisplayColor
),
child: Consumer<AppBrain>(
builder: (context,appBrain, child)=> TextField(// <<=====
controller: appBrain.textDisplayController,
scrollController: appBrain.textDisplayScrollController,
readOnly: true,
autofocus: true,
showCursor: true,
maxLines: null,
decoration: null,
cursorColor: Colors.red,
style: TextStyle(fontSize: kTextFontSize,)
),
),
),
),
),
...
Enter Button
Example of a button that adds a letter to the TextField
BottomButton(//ENTER BUTTON
onPressed: () {
var appBrain = Provider.of<AppBrain>(context, listen:false);// <<===
appBrain.addWord('\n');
},
label: 'Enter',
color: kEnterButtonColor,
)
App Brain
Relevant portions of AppBrain, a ChangeNotifier. The main summary of addWord is that I directly edit textDisplayController.text and textDisplayController.selection.
class AppBrain with ChangeNotifier {
...
TextEditingController textDisplayController = TextEditingController();
ScrollController textDisplayScrollController = ScrollController();
...
...
void addWord (String aWord){//Inserts/replaces word into text of TextDisplay
//DIRECTLY EDITS textDisplayController.text and textDisplayController.selection
String displayText = textDisplayController.text;
List<int> selectionRange = getSelectionRange();
int cursorCharIndex = selectionRange[0]; //position in text
//position in _numTChars
int newCursorDisplayIndex = getCursorDisplayIndex(cursorCharIndex);
//Update text and _numTChar.
//If there is a highlighted selection of text to be replaced,
if (selectionRange[0]!=selectionRange[1]){
int lidx = selectionRange[0];
int ridx = selectionRange[1];
int lDisplayIdx = getCursorDisplayIndex(lidx);
int rDisplayIdx = getCursorDisplayIndex(ridx);
textDisplayController.text = displayText.substring(0,lidx) + aWord; <<===
if (ridx < displayText.length) {
textDisplayController.text += displayText.substring(ridx);
}
_numTChars[lDisplayIdx] = aWord.length;
_numTChars = _numTChars.sublist(0, lDisplayIdx+1) +
_numTChars.sublist(rDisplayIdx);
}else {//Insert character at the cursor
if (displayText.length == 0 || cursorCharIndex == displayText.length) {
textDisplayController.text += aWord;
} else {
textDisplayController.text = displayText.substring(0, cursorCharIndex)+
aWord + displayText.substring(cursorCharIndex);
}
_numTChars.insert(newCursorDisplayIndex, aWord.length);
}
//place the cursor in the correct position.
cursorCharIndex += aWord.length;
// <<===
textDisplayController.selection = TextSelection(
baseOffset: cursorCharIndex,
extentOffset: cursorCharIndex
);
notifyListeners();
}
...
}
When keyboard pops up it covers bottom of you widget. you should use MediaQuery to get bottom padding after keyboard pops up.
wrap your page in a Padding widget and set the bottom like this.
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: YourWidget()
when keyboard is not visible the value of
MediaQuery.of(context).viewInsets.bottom is zero, but when keyboard pops up the value changes to somethong like 253.6.
For example, in a TextField it is possible to set an InputDecoration with a label text, that shows in the center of the TextField when there is no user input, and then shows above the user's input text afterward.
With a DropDownButton I can only seem to get the hint text to display before the user makes a selection, and then it disappears and only displays the user's selection. Is there a way to mimic the TextField's behavior?
Thanks!
You can achieve that using DropDownButtonFormField widget instead of DropDownButton. DropDownButtonFormField has decoration property which lets you use labelText which goes over the top of the field after selecting an item from the list. Sample working code below:
return DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'select option'
),
value: selected,
items: ["A", "B", "C"]
.map((label) => DropdownMenuItem(
child: Text(label),
value: label,
))
.toList(),
onChanged: (value) {
setState(() => selected = value);
},
);
Output:
Hope this answers your question.
I have a form with multiple text fields and would like users to be able to jump to the next input field when tapping "enter" on the on-screen keyboard.
I have been able to make it work by requesting focus for the next field's FocusNode in my field's onFieldSubmitted handler:
new TextFormField(
...
onFieldSubmitted: (newValue) {
...
FocusScope.of(context).requestFocus(
widget.nextNode ?? new FocusNode()
);
}
This works, but you do briefly see the on-screen keyboard close and re-open. Is there a way in Flutter to keep the keyboard on the screen until widget.nextNode becomes null?
Try adding below parameter to TextFormField.
textInputAction: TextInputAction.next,