Flutter Use Case: Make TextField without Controller

Veli Bacık
4 min readApr 20, 2023


Hello and welcome to my upcoming series. Solutions to business life problems will be shared in this series. The topic for today is “managing your text fields without using text controllers”. Let’s get started

Use Case 🔎

We need a text field for the home screen, and we want it to be cleared when the user presses the icon button.

Idea 🧩

Developers typically think like this: “I can create a textfield and it will take a controller. When the user presses a controller button, the text field will be cleared by using the controller instance.”. This idea is actually true, but there is one aspect that is missing. Everyone can definitely clear the field with a controller, but they can also do many other things as well. You can change text, dispose, or change the selection position, for example. Tasks, however, are only clear texts.

It will be perfect if you close your controller methods or limit your controller functions, and your task will be completely completed. You have a clear method for your structure, so your team is unable to do other things.

Code ✏️

Actually my thoughts are always the same, there is never a single solution to a request, but you must have an idea for the request. Creating an action listener structure is my plan. This structure helps us listen for user requests.

The first step is to create a custom action:

class ClearTextAction extends Action<ClearTextIntent> {
String text = '';
void invoke(ClearTextIntent intent) {
final value = intent.text;
if (value != null && value.isNotEmpty) {
text = value;
} else {
text = '';

The code is so clear. This is an important point inside invoke. The function is to control and notify all listeners when something changes. The structure works like an observer pattern. Intent class is another point to consider. Logicical operations are created in the class. It’s just a text value this time, but it can get more parameters.

 class ClearTextIntent extends Intent {
final String? text;

const ClearTextIntent({this.text});

The intent and action structures are ready for the use case scenario. For these processes, it is necessary to create a manage layer before using login view. Manager class decides what to do.

The Manager class has two methods for creating. The first method is to clear, and the second is to update. When you clear a field, it will remove all values, and when you update it, it will add new text to it.

class ControllerCleaner {
final ClearTextAction _clearTextAction;

ControllerCleaner(ClearTextAction clearTextAction) : _clearTextAction = clearTextAction;

void clear() {
_clearTextAction.invoke(const ClearTextIntent());

void update(String title) {
_clearTextAction.invoke(ClearTextIntent(text: title));

The time has come for the login view. Think about how to make this view first. This is what I think should be done:

  • Login View
    - Floating Button (bottom of screen)
    - — Update button
    - — Clear button
    - AppBar
    - _CustomTextField (a sub-widget)

When this class is initializing, it needs the create action and custom controller manager. Following initialization, it passes the instance to the custom texfield component. A custom component listens for actions and notifies all changes to root views based on business requirements.

It is possible to call the clear method of the manager class from the update button, and the clear button works similarly.

Let’s explore custom text field components in detail. Listening to it and making our logic based on it. The component also has a text field controller, but the user or root can’t access it. This field will use controller for normal operation and listen to our main action. The logic is working when the main action sends a new notification and performs its operations.

Key? key,
required this.action,
required this.onChange,
}) : super(key: key);

final TextEditingController controller = TextEditingController();
final ClearTextAction action;

The action listener starts working after passing these values.

Widget build(BuildContext context) {
return ActionListener(
action: action,
listener: (action) {
if (action is ClearTextAction) {
if (action.text.isEmpty) {
} else {
controller.text = action.text;
child: TextField(
onChanged: onChange,
controller: controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),

In the end, we reached our main goal. Everything we can do can be done for this perspective. Here is only a sample of what we are able to do.

Thank you for reading

Enjoy coding!




Veli Bacık

We always change the world, so just want it. [OLD]Google Developer Expert Flutter & Dart, Gamer, Work More!