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:
TextField
is entered, the BLoC/Cubit is updated accordinglyTextField
's content is updated accordinglyTextField
losing its focusTextField
can have an initial value but does not have to be focussed on renderI could not find a solution despite having researched for quite some time now.
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);
}