Reputation: 1071
While using BLoC library we store all the variables in a state class. But where to store TextEditingController, which does not change, but the value of it does?
Let's say I have a state class like this (Just as example):
@freezed
abstract class EditItemState with _$EditItemState {
const factory EditItemState.updated({
TextEditingController titleController,
ShoppingItem shoppingItem,
}) = _ShoppingListLoaded;
}
And the Cubit class:
class EditItemCubit extends Cubit<EditItemState> {
EditItemCubit() : super(EditItemState.updated());
Future<void> titleUpdated() async {
emit(
EditItemState.updated().copyWith(
shoppingItem: state.shoppingItem.copyWith(
title: state.titleController.text,
),
),
);
}
}
So the Cubit class logic looks messy. I suggest to keep such controllers directly in the widget or in BLoC/Cubit class. Is it a correct approach?
Upvotes: 23
Views: 12913
Reputation: 24167
The key to this problem is that the TextEditingController's value should be updated in the UI by the Bloc's state and only the Bloc's state.
A BlocListener surrounds the TextFormField. It checks if the state has changed and if it has then set the controller value.
child: BlocListener<MyBloc, MyState>(
listener: (context, state) {
var newText = widget.getState(state).value ?? '';
if (_controller.text != newText) //
_controller.text = newText;
},
Setting the TextEditingController's value changes the cursor position inside the field; that is why do a check to see if the value has changed. Otherwise whenever we enter text in the TextFormField we will trigger a state change and the controller text will get updated changing the position of the cursor.
Upvotes: 0
Reputation: 11
I think keeping it inside Bloc Cubit would be a great option because lets say you have separated Widgets in a page with sections i.e. General Sections, intro sections, in a form. Imagine passing TextEditingController to all the child widget from the parent Widgets. Just keep inside cubit or bloc, wrap the parents widget with blocbuilder and read from anywhere.
Upvotes: 0
Reputation: 9726
I would advice agains using TextEditingControllers
in blocs or cubits, because for me it's a UI level which shouldn't be passed to the logic above.
The problem with either Animation or Text controllers goes away if you use hooks. The code inside of a HookWidget
looks like this:
final titleTextController = useTextEditingController(text: book?.title ?? '');
I can add listeners to it and call some methods on change or just get current text and don't care about disposal, as it's happening automatically.
Other hooks include useContext()
or useState()
. More on hooks in my Flutter cubits + hooks tutorial.
Upvotes: 0
Reputation: 1638
Here guys asked the same question from the library authors and the answer of Felix Angelov (author of flutter_bloc) is:
I would highly recommend against maintaining TextEditingController as part of the bloc. Blocs should ideally be platform-agnostic and have no dependency on Flutter. If you need to use TextEditingControllers I would recommend creating a StatefulWidget and maintaining them as part of the State class. Then you can interface with the control in response to state changes via BlocListener
Upvotes: 37
Reputation: 3945
Personally, I have kept mine within my Cubit class. The reason for that is because I am more than likely going to be using the result of that controller at some point. To keep things clean I reference the controller's text within the Cubit rather than pass the text through an event.
Another reason is because you are able to subscribe to events, like addListener
, of the controller within the Cubit, which would be considered "Business Logic".
Upvotes: 3