164

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?

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Daniel
  • 2,223
  • 3
  • 9
  • 18

13 Answers13

272

Simply change the text property

    TextField(
      controller: txt,
    ),
    RaisedButton(onPressed: () {
        txt.text = "My Stringt";
    }),

while txt is just a TextEditingController

  var txt = TextEditingController();
Raouf Rahiche
  • 28,948
  • 10
  • 85
  • 77
83

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.

rfarias
  • 1,296
  • 10
  • 15
  • 4
    This should be the right answer, since it deals with the problem of changing the controller's value and the cursor coming back to the start of the text all the time – Jose Jet Oct 25 '19 at 09:45
  • 10
    One more suggestion, when providing offset, we can do: int currentOffset = _textEditingController.selection.base.offset; And then provide currentOffset as offset. This will also handle the case when the user is in between the string. – Abhinav Apr 20 '20 at 11:23
52

Screenshot:

enter image description here

  1. Create a TextEditingController:

    final TextEditingController _controller = TextEditingController();
    
  2. Assign it to your TextField or TextFormField:

    TextField(controller: _controller)
    
  3. 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),
        );
      },
    )
    
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
30

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";
       }
    ),
Twitter khuong291
  • 11,328
  • 15
  • 80
  • 116
Chaythanya Nair
  • 4,774
  • 6
  • 32
  • 40
14
_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.

Daniel
  • 2,223
  • 3
  • 9
  • 18
11

First Thing

  TextEditingController MyController= new TextEditingController();

Then add it to init State or in any SetState

    MyController.value = TextEditingValue(text: "ANY TEXT");
Dharman
  • 30,962
  • 25
  • 85
  • 135
Muhammad Ashraf
  • 3,323
  • 2
  • 12
  • 19
9

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"),
        ),
      ],
    );
  }
}
Oleg Novosad
  • 2,261
  • 1
  • 27
  • 28
5

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:

enter image description here

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:

  1. There is a selection (delete the selection)
  2. The cursor is at the beginning (don’t do anything)
  3. 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.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • I am using this code which is doing basically adding value at the start when value of textField is not null but problem is when it added $ sign after an input position of cursor changes and starts writing before value like if previous value was $1 then it writes 2$1. So is there any solution or better logic for this task? " `onChanged: (value) { if (dollarSignAdded == false) { priceTextEditingController.value=TextEditingValue( text: "\$" +priceTextEditingController.text); dollarSignAdded = true; } else if (value.isEmpty) { dollarSignAdded = false; } }`" – Arslan Kaleem May 18 '21 at 08:56
1
  1. Declare TextEditingController.

  2. supply controller to the TextField.

  3. user controller's text property to change the value of the textField.

follow this official solution to the problem

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
1

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,),
      ]),
    );
  }
}
live-love
  • 48,840
  • 22
  • 240
  • 204
0

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

0

simply add labelText to InputDecoration

TextField(
  controller: _controller,
  decoration: InputDecoration(labelText: 'init text'),
),
-1

add text to your controller like this image below

enter image description here

Ejeh
  • 425
  • 5
  • 7