TextField and BLoC – value not updated or keyboard dismissed

Issue

I want to use a TextField in combination with BLoC package in Flutter. The aim is to keep the content of the TextField in sync with the respective property of the BLoC.

Because BLoC is based on a BlocBuilder widget that basically subscribes to a Stream and updates and re-renders all affected child widgets when the Stream emits something new, I thought the following approach would be sufficient:

BlocBuilder<MyCubit, MyState>(
    builder: (BuildContext context, MyState state) {
        TextFormField(
          initialValue: state.model.title,
          onChanged: (String value) => myCubit.setTitle(value),
        ),
    }
);

And actually, it’s working. The problems appear when I want to be able to override the value from the BLoC. In my case, I have a TextField and a select field (DropdownButton). When the user selects a value from the DropdownButton, the content of the TextField is supposed to be overridden.
With the above approach, when I call setTitle(value) in the onChange of the DropdownButton, the value in the Cubit is overridden, but the TextField is left unchanged.

Then I had the idea to use a TextEditingController that handles the text of the TextField (like it’s suggested here by Felix Angelov), updates the Cubit on change and listens to external changes to the respective value in the Cubit:

BlocConsumer<MyCubit, MyState>(
    listener: (BuildContext context, MyState state) {
        if (state is InitialState) {
            _controller.addListener(() {
              myCubit.setTitle(_controller.text);
            });
            return;
        }

        _controller.text = state.title;
    },
    builder: (BuildContext context, MyState state) {
        TextFormField(
            controller: _controller,
        ), 
    }
);

If I do this, then the initial value is correctly set by the Cubit, the value is updated in the TextField when I change it using the DropdownButton and it’s updated in the Cubit when the text in the TextField changes. However, there is one problem: every time I enter a character inside the TextField, it loses its focus.
autofocus: true to the rescue? Unfortunately not. This prevents to lose focus, but then it jumps to the first character of the entered text whenever I enter something. Also, I don’t want the TextField to have focus initially. Setting a FocusNode also does not help.
Even setting a UniqueKey like it’s suggested here does not change something.
The ideas presented here regarding FocusNode and onEditingComplete did not solve my problem either.

Does anyone have a working solution for a TextField in combination with BLoC that fulfills the following requirements:

  • When something in the TextField is entered, the BLoC/Cubit is updated accordingly
  • When the BloC/Cubit is updated, the TextField‘s content is updated accordingly
  • There is no unexpected, annoying keyboard dismiss behavior or TextField losing its focus
  • The TextField can have an initial value but does not have to be focussed on render

I could not find a solution despite having researched for quite some time now.

Solution

In your later attempt to change TextField text using TextEditingController why your TextField is wrapped inside BlocBuilder? I believe this would work correctly for updating TextField‘s text from your BLoC:

BlocListener<MyCubit, MyState>(
  listener: (BuildContext context, MyState state) {
    if(state.title != _controller.text) {
      _controller.text = state.title;
      _controller.selection = TextSelection.collapsed(offset: state.title.length);
    }
  },
  child: TextFormField(
    controller: _controller,
  ),
);

And for updating value inside of your BLoC with events coming from your TextField, register a listener to the TextEditingController in your initState.

@override
void initState() {
  super.initState();
  _controller.addListener(_changed);
}

@override
void dispose() {
  _controller.removeListener(_changed);
  super.dispose();
}

_changed() {
  myCubit.setTitle(_controller.text);
}

Answered By – Amir_P

Answer Checked By – David Marino (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *