Jerry Woo CW
Jerry Woo CW

Reputation: 31

Flutter: TextFormField: Cursor resets to beginning after calling setState()

I'm making a question answer application to pass time.

There are questions in a question list. The code below shows for each question. A question can have multiple choices but only 1 answer.

I'm having trouble where everytime I type in the TextFormField, the cursor resets to the beginning. How can i avoid that? I need to use setState so that the Text Widget updates in realtime.

class Question{
  int answer;
  List<Answer> answers;
}

class Answer{
  int index;
  String answer;
}

class EditQuestionWidget extends StatefulWidget {
  final List<Question> questions;
  final int index;

  EditQuestionWidget(this.questions, this.index);

  @override
  _EditQuestionWidgetState createState() => _EditQuestionWidgetState();
}

class _EditQuestionWidgetState extends State<EditQuestionWidget> {
  @override
  Widget build(BuildContext context) {

    return Card( //For Each Question
      child: ExpansionTile(
        title: Text(widget.questions[widget.index].answer != null ? widget.questions[widget.index].answers[widget.questions[widget.index].answer].answer ?? 'Untitled Answer' : 'Untitled Answer'),
        children: <Widget>[

          ListView.builder( //For Each answer choice
            itemCount: widget.questions[widget.index].answers.length,
            itemBuilder: (context, i) {

              TextEditingController answerController = TextEditingController(text: widget.questions[widget.index].answers[i].answer);

              return Row(
                children: <Widget>[
                  Expanded(
                    child: RadioListTile( //Select Answer
                      value: widget.questions[widget.index].answers[i].index,
                      groupValue: widget.questions[widget.index].answer,
                      onChanged: (int value) => setState(() => widget.questions[widget.index].answer = value),
                      title: TextFormField( //change answer choice text
                        controller: answerController,
                        onChanged: (text) {
                          setState(() {widget.questions[widget.index].answers[i].answer = text;}); // The cursor returns to beginning after executing this.
                        },
                      ),
                    ),
                  ),
                  IconButton( //Remove Answer Function
                    onPressed: () {

                      //This method Works as intended.

                      if (widget.questions[widget.index].answers[i].index == widget.questions[widget.index].answer) widget.questions[widget.index].answer = null;
                      String answer = widget.questions[widget.index].answer != null ? widget.questions[widget.index].answers[widget.questions[widget.index].answer].answer : '';
                      widget.questions[widget.index].answers.remove(widget.questions[widget.index].answers[i]);

                      if (widget.questions[widget.index].answers.isEmpty) {
                        widget.questions[widget.index].answer = widget.questions[widget.index].answers.length;
                        widget.questions[widget.index].answers.add(new Answer(index: widget.questions[widget.index].answers.length));
                      }

                      for (int ii = 0; ii < widget.questions[widget.index].answers.length; ii++) {
                        widget.questions[widget.index].answers[ii].index = ii;
                        if (widget.questions[widget.index].answer != null && answer == widget.questions[widget.index].answers[ii].answer) widget.questions[widget.index].answer = ii;
                      }

                      setState(() {});
                    },
                  ),
                ],
              );

            },
          ),

        ],
      ),
    );

  }
}


Upvotes: 3

Views: 1054

Answers (1)

Try
Try

Reputation: 3469

You're updating the value of answerController every time the text changes, and when this update happens the cursor goes to the beginning.

To solve that you can create a list of TextEditingControllers outside the builder:

List<TextEditingController> controllers = [];

And then add a new Controller to the list when a new field is created:

itemBuilder: (context, i) {
            if(controllers.length != widget.questions[widget.index].answers.length){
              controllers.add(TextEditingController());  //condition to prevent the creation of infinity Controllers
            }

And finally pass the controller to the TextField:

controller: controllers[i],

Upvotes: 1

Related Questions