How do you change the value inside of a textfield flutter? - flutter

I have a TextEditingController where if a user clicks a button it fills in with information. I can't seem to figure out how to change the text inside of a Textfield or TextFormField. Is there a solution?

Simply change the text property
TextField(
controller: txt,
),
RaisedButton(onPressed: () {
txt.text = "My Stringt";
}),
while txt is just a TextEditingController
var txt = TextEditingController();

The problem with just setting
_controller.text = "New value";
is that the cursor will be repositioned to the beginning (in material's TextField). Using
_controller.text = "Hello";
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length),
);
setState(() {});
is not efficient since it rebuilds the widget more than it's necessary (when setting the text property and when calling setState).
--
I believe the best way is to combine everything into one simple command:
final _newValue = "New value";
_controller.value = TextEditingValue(
text: _newValue,
selection: TextSelection.fromPosition(
TextPosition(offset: _newValue.length),
),
);
It works properly for both Material and Cupertino Textfields.

Screenshot:
Create a TextEditingController:
final TextEditingController _controller = TextEditingController();
Assign it to your TextField or TextFormField:
TextField(controller: _controller)
To update the text using a button at the cursor position (imagine there is already text in the textfield) :
ElevatedButton(
onPressed: () {
const newText = 'Hello World';
final updatedText = _controller.text + newText;
_controller.value = _controller.value.copyWith(
text: updatedText,
selection: TextSelection.collapsed(offset: updatedText.length),
);
},
)

You can use the text editing controller to manipulate the value inside a textfield.
var textController = new TextEditingController();
Now, create a new textfield and set textController as the controller for the textfield as shown below.
new TextField(controller: textController)
Now, create a RaisedButton anywhere in your code and set the desired text in the onPressed method of the RaisedButton.
new RaisedButton(
onPressed: () {
textController.text = "New text";
}
),

_mytexteditingcontroller.value = new TextEditingController.fromValue(new TextEditingValue(text: "My String")).value;
This seems to work if anyone has a better way please feel free to let me know.

First Thing
TextEditingController MyController= new TextEditingController();
Then add it to init State or in any SetState
MyController.value = TextEditingValue(text: "ANY TEXT");

The issue does not appear if you use the StatefulWidget with _controller as a member. Sounds weird but moving from stateless to stateful worked fine (that's because the widget is redrawn on each input to text editing controller which does not preserve state) E.g.:
Stateful: (working)
class MyWidget extends StatefulWidget {
const MyWidget(
{Key? key})
: super(key: key);
#override
_MyWidgetState createState() =>
_MyWidgetState();
}
class _MyWidgetState
extends State<MyWidget> {
late TextEditingController _controller;
#override
void initState() {
super.initState();
_controller = TextEditingController(text: "My Text");
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _controller,
),
],
);
}
}
Stateless: (issue)
class MyWidget extends StatelessWidget {
const MyWidget(
{Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: TextEditingController(text: "My Text"),
),
],
);
}
}

If you simply want to replace the entire text inside the text editing controller, then the other answers here work. However, if you want to programmatically insert, replace a selection, or delete, then you need to have a little more code.
Making your own custom keyboard is one use case for this. All of the inserts and deletions below are done programmatically:
Inserting text
The _controller here is a TextEditingController for the TextField.
void _insertText(String myText) {
final text = _controller.text;
final textSelection = _controller.selection;
final newText = text.replaceRange(
textSelection.start,
textSelection.end,
myText,
);
final myTextLength = myText.length;
_controller.text = newText;
_controller.selection = textSelection.copyWith(
baseOffset: textSelection.start + myTextLength,
extentOffset: textSelection.start + myTextLength,
);
}
Thanks to this Stack Overflow answer for help with this.
Deleting text
There are a few different situations to think about:
There is a selection (delete the selection)
The cursor is at the beginning (don’t do anything)
Anything else (delete the previous character)
Here is the implementation:
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 newStart = textSelection.start - 1;
final newEnd = textSelection.start;
final newText = text.replaceRange(
newStart,
newEnd,
'',
);
_controller.text = newText;
_controller.selection = textSelection.copyWith(
baseOffset: newStart,
extentOffset: newStart,
);
}
Full code
You can find the full code and more explanation in my article Custom In-App Keyboard in Flutter.

Here is a full example where the parent widget controls the children widget. The parent widget updates the children widgets (Text and TextField) with a counter.
To update the Text widget, all you do is pass in the String parameter.
To update the TextField widget, you need to pass in a controller, and set the text in the controller.
main.dart:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: Home(),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Update Text and TextField demo'),
),
body: ParentWidget());
}
}
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _counter = 0;
String _text = 'no taps yet';
var _controller = TextEditingController(text: 'initial value');
void _handleTap() {
setState(() {
_counter = _counter + 1;
_text = 'number of taps: ' + _counter.toString();
_controller.text = 'number of taps: ' + _counter.toString();
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(children: <Widget>[
RaisedButton(
onPressed: _handleTap,
child: const Text('Tap me', style: TextStyle(fontSize: 20)),
),
Text('$_text'),
TextField(controller: _controller,),
]),
);
}
}

Declare TextEditingController.
supply controller to the TextField.
user controller's text property to change the value of the textField.
follow this official solution to the problem

simply change the text or value property of controller.
if you do not edit selection property cursor goes to first of the new text.
onPress: () {
_controller.value=TextEditingValue(text: "sample text",selection: TextSelection.fromPosition(TextPosition(offset: sellPriceController.text.length)));
}
or in case you change the .text property:
onPress: () {
_controller.text="sample text";
_controller.selection = TextSelection.fromPosition(TextPosition(offset:_controller.text.length));
}
in cases that do not matter to you just don't change the selection property

add text to your controller like this image below

Related

Flutter - How to implement inline editing?

I found a similar behaviour on Safari:
The problem statement is that on tap of normal text, the text field should be editable and we can able to edit the text.
Have an example
In this scenario, we can see how on tap of initial text the editable text field is getting displayed and we can able to edit the text.
So let’s start with the process:
First, we have to initialize the variables.
bool _isEditingText = false;
TextEditingController _editingController;
String initialText = "Initial Text";
_isEditingText is the boolean variable and we have to set it false because we have to make it true when the user is tap on text.
TextEditingController -whenever a user modifies a text field with an associated TextEditingController, the text field edits and the controller notifies the listener. Listeners can then read the text and selection properties that the user has typed and updated. Basically the text editing controller is used to get the updated value from the text field.
initialText -Initial value, set to the text.
When we use any type of controller then we have to initialize and dispose of the controller.
So first initialize the controller in init state.
#override
void initState() {
super.initState();
_editingController = TextEditingController(text: initialText);
}
#override
void dispose() {
_editingController.dispose();
super.dispose();
}
‘dispose()’ is called when the State object is removed, which is permanent.
This method is used to unsubscribe and cancel all animations, streams, etc.
The framework calls this method when this state object will never build again.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("'Editable Text"),
),
body: Center(
child: _editTitleTextField(),
),
);
}
In build widget, I am simply displaying a widget _editTitleTextField().
Widget _editTitleTextField() {
if (_isEditingText)
return Center(
child: TextField(
onSubmitted: (newValue){
setState(() {
initialText = newValue;
_isEditingText =false;
});
},
autofocus: true,
controller: _editingController,
),
);
return InkWell(
onTap: () {
setState(() {
_isEditingText = true;
});
},
child: Text(
initialText,
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
);
}
so what exactly the _editTitleTextField() widget does is, if the value of _isEditingText is false then simply show the text and on tap of text set the value of _isEditingText to true.
When _isEditingText is true then _editTitleTextField return text field. Textfield has parameter onSubmitted, so in onSubmitted method new value is assigned to initialText which is the updated value getting from _editingController.
Tada! This way we make the text editable and update the value of the text!
This can be done by having a StatefulWidget that keeps track of the state, by using a boolean for example. Let's say this bool is named editing. When editing == false you display a Text widget with the text to display, wrapped within a handler for the tap, such as a GestureDetector. When the user taps, the state is changed to editing = true. In this state you'll display a TextField with this value. You can use a TextEditingController within in this StatefulWidget and update its value there. This would allow you to up have all text selected when the user taps and changes to the editing state
I believe this is the correct answer:
class InlineEditableText extends StatefulWidget {
const InlineEditableText({
Key? key,
required this.text,
required this.style,
}) : super(key: key);
final String text;
final TextStyle style;
#override
State<InlineEditableText> createState() => _InlineEditableTextState();
}
class _InlineEditableTextState extends State<InlineEditableText> {
var _isEditing = false;
final _focusNode = FocusNode();
late String _text = widget.text;
late final TextStyle _style = widget.style;
late TextEditingController _controller;
#override
void initState() {
_controller = TextEditingController(text: _text);
_focusNode.addListener(() {
if (!_focusNode.hasFocus) {
setState(() => _isEditing = false);
} else {
_controller.selection = TextSelection(
baseOffset: 0,
extentOffset: _controller.value.text.runes.length,
);
}
});
super.initState();
}
#override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTap: () => setState(() {
_isEditing = !_isEditing;
_focusNode.requestFocus();
}),
child: TextField(
maxLines: 1,
style: _style,
focusNode: _focusNode,
controller: _controller,
onSubmitted: (changed) {
setState(() {
_text = changed;
_isEditing = false;
});
},
showCursor: _isEditing,
cursorColor: Colors.black,
enableInteractiveSelection: _isEditing,
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 0,
vertical: 4.4,
),
border: _isEditing
? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0)),
)
: InputBorder.none,
),
),
);
}
}

Get Selection of TextFormField in Flutter

My goal is to get the selected text of the TextFormField, but each time the button is pressed, the TextFormField loses its focus, and the print in the console shows only a selection between -1 and -1.
It did work a few weeks ago, did the behavior change in the latest release? I am on the stable Flutter channel. (Flutter Channel stable, 2.5.0)
This is my test example:
import 'package:flutter/material.dart';
class Play extends StatefulWidget {
const Play({Key? key}) : super(key: key);
#override
_PlayState createState() => _PlayState();
}
class _PlayState extends State<Play> {
TextEditingController _controller = TextEditingController();
FocusNode _node = FocusNode();
void _onPressed() {
TextSelection selection = _controller.selection;
print("selection.start: ${selection.start}");
print("selection.end: ${selection.end}");
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
focusNode: _node,
controller: _controller,
),
ElevatedButton(
onPressed: _onPressed,
child: Text("do something"),
),
],
),
);
}
}
Since it works fine on mobile, I'd say it's a bug in Flutter, but fear not! Welcome to the world of workarounds and temporary solutionsTM; replace your entire _onPressed method by this:
int start = -1;
int end = -1;
#override
void initState() {
super.initState();
_controller.addListener(() async {
TextSelection selection = _controller.selection;
if (selection.start > -1) start = selection.start;
if (selection.end > -1) end = selection.end;
});
}
void _onPressed() {
print(start);
print(end);
}
Let me know if something is not very clear and I'll be happy to edit the answer with further explanations.
DartPad example
The Flutter community would love you to file a bug report (if it doesn't exist already)! :D

how to focus the next text field automatically after user input 1 character in flutter

I have 4 textFormField widgets. Once the user has completed the first text field I would like to focus on the next textField automatically. Is there a way to do this in Flutter? anyone please share , thank in advance :)
This can be done in Flutter in different ways, and I'll try to share the simplest one of them. Before getting into the answer, it's worth mentioning the following issue:
Detect when delete is typed into a TextField #14809
In Flutter, backspace does not send any event when the TextField is empty (i.e. TextField.onChanged won't be called). In your case, if the user is at third field and they press backspace to return to the second field, there's no way to capture that key press without some workaround that were discussed in the linked issue. In short, you'll need to add a zero-width space character (it doesn't get rendered but is present in the String) to detect backspace events.
I mentioned this issue because I'm sharing an example that utilize the zero-width space character (zwsp for short).
In the following example, I simply created two lists that contains:
FocusNode for each field
TextEditingController for each field.
Based on the index, you can bring the focus to a specific field by calling:
FocusNode.requestFocus().
Similarly, you can remove the focus by calling FocusNode.unfocus or you can remove any focus from anywhere by calling: FocusScope.of(context).unfocus(); (in the example below, it's used after the last character is inserted to hide the keyboard).
That being said, here's a full example that you can copy and paste to try it out:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(child: CodeField()),
);
}
}
/// zero-width space character
///
/// this character can be added to a string to detect backspace.
/// The value, from its name, has a zero-width so it's not rendered
/// in the screen but it'll be present in the String.
///
/// The main reason this value is used because in Flutter mobile,
/// backspace is not detected when there's nothing to delete.
const zwsp = '\u200b';
// the selection is at offset 1 so any character is inserted after it.
const zwspEditingValue = TextEditingValue(text: zwsp, selection: TextSelection(baseOffset: 1, extentOffset: 1));
class CodeField extends StatefulWidget {
const CodeField({Key key}) : super(key: key);
#override
_CodeFieldState createState() => _CodeFieldState();
}
class _CodeFieldState extends State<CodeField> {
List<String> code = ['', '', '', ''];
List<TextEditingController> controllers;
List<FocusNode> focusNodes;
#override
void initState() {
// TODO: implement initState
super.initState();
focusNodes = List.generate(4, (index) => FocusNode());
controllers = List.generate(4, (index) {
final ctrl = TextEditingController();
ctrl.value = zwspEditingValue;
return ctrl;
});
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// give the focus to the first node.
focusNodes[0].requestFocus();
});
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
focusNodes.forEach((focusNode) {
focusNode.dispose();
});
controllers.forEach((controller) {
controller.dispose();
});
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
4,
(index) {
return Container(
width: 20,
height: 20,
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: TextField(
controller: controllers[index],
focusNode: focusNodes[index],
maxLength: 2,
keyboardType: TextInputType.number,
decoration: InputDecoration(
counterText: "",
),
onChanged: (value) {
if (value.length > 1) {
// this is a new character event
if (index + 1 == focusNodes.length) {
// do something after the last character was inserted
FocusScope.of(context).unfocus();
} else {
// move to the next field
focusNodes[index + 1].requestFocus();
}
} else {
// this is backspace event
// reset the controller
controllers[index].value = zwspEditingValue;
if (index == 0) {
// do something if backspace was pressed at the first field
} else {
// go back to previous field
controllers[index - 1].value = zwspEditingValue;
focusNodes[index - 1].requestFocus();
}
}
// make sure to remove the zwsp character
code[index] = value.replaceAll(zwsp, '');
print('current code = $code');
},
),
);
},
),
);
}
}
You may want to use a FocusNode on each of your TextFormField, this way, once your user has enter text in the TextFormField, you can use in the callback onChanged of the TextFormField call myNextTextFieldFocusNode.requestFocus()
FocusNode textFieldOne = FocusNode();
FocusNode textFieldTwo = FocusNode();
// ...
TextFormField(
onChanged: (_) {
textFieldTwo.requestFocus();
},
focusNode: textFieldOne,
controller: textController,
)
You can use onChanged and nodefocus properties. When onchanged called refer to next textfield.
init a focus node ;
late FocusNode myFocusNode;
#override
void initState() {
super.initState();
myFocusNode = FocusNode();
}
#override
void dispose() {
// Clean up the focus node when the Form is disposed.
myFocusNode.dispose();
super.dispose();
}
onChanged property;
TextField(
focusNode: myFocusNode1,
onChanged: (text) {
myFocusNode2.requestFocus();// I could not remember the correct usage please check
},
),

Flutter - TextField is empty every time I re-open the widget

So, I'm new to Flutter and was trying to code a simple notes app to learn my way around it. The layout of the app is a HomePage with a ListView of NoteTiles that when tapped open a corresponding NotePage where you can write down things. The issue I got is that every time I leave the NotePage and then re-open it from the HomePage, the NotePage loses its content.
My first idea was to keep the content in the corresponding NoteTile so that when I leave the NotePage I would pop with the content, and when needed I would push to the NotePage with the previously saved content. The problem is that I didn't find any simple way to push and set the content. I've seen there are Notification and Route methods but they come with quite a lot of boilerplate and they look like it's more for passing data from child to parent, which I can do easily when popping.
So, is there a way to avoid the reset of the content of the NotePage? Or maybe is there a simple way to initState with the content previously saved?
Here is the code I have so far:
class NoteTile extends ListTile {
final NotePage note;
final Text title;
final BuildContext context;
NoteTile(this.title, this.note, this.context) : super(
title: title,
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => note),
),
},
onLongPress: () => null,
);
void switchToNote() async {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => note),
);
}
}
onLongPress will later be used to delete the note.
class NotePage extends StatefulWidget {
final String title;
NotePage({Key key, this.title}) : super(key: key);
#override
_NotePageState createState() => _NotePageState();
}
class _NotePageState extends State<NotePage> {
TextEditingController _controller;
String _value;
void initState() {
super.initState();
_controller = TextEditingController();
_controller.addListener(_updateValue);
_value = '';
}
void dispose() {
_controller.dispose();
super.dispose();
}
void _updateValue(){
_value = _controller.text;
}
Future<bool> _onWillPop() async {
Navigator.pop(context, _value);
return true;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(fontStyle: FontStyle.italic),
hintText: 'New note',
),
maxLines: null,
controller: _controller,
),
padding: EdgeInsets.all(12.0),
),
),
onWillPop: _onWillPop,
);
}
}
The _onWillPop is to send back the content to the NoteTile, which currently disregards the return data because I failed to find a way to use that data later when pushing the NotePage again.
ListTile is StatelessWidget widget, so you cannot reserve the state, the NoteTile should be StatefulWidget.
Here is a sample of NoteTile:
class NoteTile extends StatefulWidget {
final Text title;
const NoteTile({Key key, this.title}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _NoteTileState();
}
}
class _NoteTileState extends State<NoteTile> {
String _result;
#override
void initState() {
super.initState();
_result = "";
}
void _onOpenNote() async {
String result = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NotePage(title: _result)),
);
if (result != null) {
_result = result;
}
}
#override
Widget build(BuildContext context) {
return ListTile(
title: widget.title,
onTap: _onOpenNote,
);
}
}
And NotePage should be edit in some lines:
TextEditingController can have initialize string & have to initialize by title:
_controller = TextEditingController(text: widget.title);
Navigator.pop can post result and you did it correctly, but if you want to get the result, should wait for Navigator.of(context).push, because its run in another thread.

Show TextField value on same screen on button click

This is my screen with TextField and Button. When someone clicks on show button, I want it to show the name below the button as shown in below picture.
Code below:
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final name = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Row(
children: [
Text(
'Name'
),
TextField(
controller: name,
)
],
),
RaisedButton(
onPressed: (){
},
child: Text('Show'),
)
],
),
),
);
}
}
This can be a basic example for your question. The UI is not exactly what you've shown above
class Question extends StatefulWidget {
Question({Key key}) : super(key: key);
#override
_QuestionState createState() => _QuestionState();
}
class _QuestionState extends State<Question> {
String text = '';
bool shouldDisplay = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Center(
child: TextField(
onChanged: (value) {
setState(() {
text = value;
});
},
),
),
FlatButton(onPressed: () {
setState(() {
shouldDisplay = !shouldDisplay;
});
}, child: Text('Submit')),
shouldDisplay ? Text(text) : Spacer()
],
);
}
}
Hope this helps.
Initialize two variables. One for a TextEditingController and one for the text value.
TextEditingController controller = TextEditingController();
String display = '';
Give your TextField a controller.
TextField(controller:controller);
In your button, set onPressed to change display text to the controller text.
FlatButton(
child: Text("Show"),
onPressed()=> setState((){display = controller.text;});
),
Then where you want to show the text, set the text string to display.
Text(display);
I would advice you to learn the basics of flutter first before asking these kinds of questions. This can be simply achieved through using TextEditingController and setState(). Simply define a controller for your TextField and then call setState() when your button is pressed. Note that you have to be on a StatefulWidget since calling setState() rebuilds the UI.
Create a TextEditingController and string above the #override Widget build:
String displayName="";
final myController = TextEditingController();
Create a TextField and add assign the controller to it:
TextField(
controller: myController,
);
Call setState() on button pressed:
MaterialButton(
child: Text("Show"),
onPressed: (){
setState(() {
displayName=myController.text;
});
})
Display it using a Text widget:
Text(displayName);
Good Luck!
You can find out how to use TextEditingController here: https://flutter.dev/docs/cookbook/forms/retrieve-input
More about widgets here: https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG