Is there a proper way to add a clear button to the TextField
?
Just like this picture from Material design guidelines:
What I found is
If you want a ready-to-use Widget, which you can just put in a file and then have a reusable element you can use everywhere by inserting ClearableTextField(), use this piece of code:
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),
);
}
}
Further explanations can be found on this page:
https://www.flutterclutter.dev/flutter/tutorials/text-field-with-clear-button/2020/104/
It also builds upon IconButton, but has the advantage of only displaying the clear button when there is text inside the textfield.
Looks like this:
suffixIcon: IconButton(
icon: Icon(
Icons.cancel,
),
onPressed: () {
_controllerx.text = '';
}
),
Output:
Create a variable
var _controller = TextEditingController();
And your TextField
:
TextField(
controller: _controller,
decoration: InputDecoration(
hintText: "Enter a message",
suffixIcon: IconButton(
onPressed: () => _controller.clear(),
icon: Icon(Icons.clear),
),
),
)
Here's a snippet of my code that works.
What it does: only show clear button if text field value is not empty
class _MyTextFieldState extends State<MyTextField> {
TextEditingController _textController;
bool _wasEmpty;
@override
void initState() {
super.initState();
_textController = TextEditingController(text: widget.initialValue);
_wasEmpty = _textController.text.isEmpty;
_textController.addListener(() {
if (_wasEmpty != _textController.text.isEmpty) {
setState(() => {_wasEmpty = _textController.text.isEmpty});
}
});
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _textController,
decoration: InputDecoration(
labelText: widget.label,
suffixIcon: _textController.text.isNotEmpty
? Padding(
padding: const EdgeInsetsDirectional.only(start: 12.0),
child: IconButton(
iconSize: 16.0,
icon: Icon(Icons.cancel, color: Colors.grey,),
onPressed: () {
setState(() {
_textController.clear();
});
},
),
)
: null,
),);
...
Didn't want to go the StatefulWidget route. Here's an example using TextEditingController and StatelessWidget (with Providers pushing updates). I keep the controller in the static field.
class _SearchBar extends StatelessWidget {
static var _controller = TextEditingController();
@override
Widget build(BuildContext context) {
var dictionary = Provider.of<Dictionary>(context);
return TextField(
controller: _controller,
autofocus: true,
onChanged: (text) {
dictionary.lookupWord = text;
},
style: TextStyle(fontSize: 20.0),
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search',
suffix: GestureDetector(
onTap: () {
dictionary.lookupWord = '';
_controller.clear();
},
child: Text('x'),
)));
}
}
IconButton(
icon: Icon(Icons.clear_all),
tooltip: 'Close',
onPressed: () {
},
)