Text field: Add trailing clear button

The typical app user is used to having the possibility to clear the whole text inside of a text field with one tap. Both iOS and Android natively display a respective button at the end of the text field whenever the user starts typing.

This functionality is not mandatory and so Flutter does not provide it to TextFields by default but makes it very easy to implement it.

Let’s setup a simple app with a TextField at the bottom:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
        title: Text(widget.title),
      ),
        body: Align(
          alignment: Alignment.bottomCenter,
          child: Container(
            padding: EdgeInsets.all(16),
            child: _getTextField()
          )
        ),
      );
    }

  TextField _getTextField() {
    return TextField(
      controller: _textEditingController,
      decoration: InputDecoration(
        hintText: "Enter a beautiful text"
      ),
    );
  }
}
Screen with a signle TextField at the bottom
An app with a single TextField

It gives us this simple screen.

In order to display something that’s located at the end of the TextField, we can use the suffixIcon argument of the widget. The title is misleading to a certain degree as it’s not necessarily an icon. In fact, the expected type is a Widget. So we’re not limited in any way:

Show it only when there’s text

That also enables us to easily show the button only under certain conditions. In this case: when the text field is not empty:

bool _showClearButton = false;

@override
void initState() {
  super.initState();
  _textEditingController.addListener(() {
    setState(() {
      _showClearButton = _textEditingController.text.length > 0;
    });
  });
}

TextField _getTextField() {
  return TextField(
    controller: _textEditingController,
    decoration: InputDecoration(
      hintText: "Enter a beautiful text",
      suffixIcon: _getClearButton(),
    ),
  );
}

Widget _getClearButton() {
  if (!_showClearButton) {
    return null;
  }

  return IconButton(
    onPressed: () => _textEditingController.clear(),
    icon: Icon(Icons.clear),
  );
}

So what I did:

  • I added a variable _showClearButton that’s storing whether the clear button is to be shown or not
  • In the setState() method I attached a listener to the controller of the TextField that updates the above mentioned value once there was a change
  • Lastly, I created a method that returns a button. If _showClearButton is false, it returns null, resulting in the button not being displayed. Otherwise, the button is displayed. onPressed has a function assigned to clear the text

What does it look like?

Animation showing TextField with clear button in action
TextField with clear button in action

As a standalone Widget

If you want a standalone Widget which you can use everywhere by typing ClearableTextfield(), use this piece of code and ideally put it in a separate file called clearable_textfield.dart:

import 'package:flutter/material.dart';

class ClearableTexfield extends StatefulWidget {
  ClearableTexfield({
    Key key,
    this.controller,
    this.hintText = 'Enter text'
  }) : super(key: key);

  final TextEditingController controller;
  final String hintText;

  @override
  State<StatefulWidget> createState() {
    return _ClearableTextfieldState();
  }
}

class _ClearableTextfieldState extends State<ClearableTexfield> {
  bool _showClearButton = false;

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(() {
      setState(() {
        _showClearButton = widget.controller.text.length > 0;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: widget.controller,
      decoration: InputDecoration(
        hintText: widget.hintText,
        suffixIcon: _getClearButton(),
      ),
    );
  }

  Widget _getClearButton() {
    if (!_showClearButton) {
      return null;
    }

    return IconButton(
      onPressed: () => widget.controller.clear(),
      icon: Icon(Icons.clear),
    );
  }
}

If you like what you’ve read, feel free to support me:

🥗Buy me a salad

Leave a Comment