I want to use a TextField
to allow the user to rename an item in the app. I would like for the new item name to be saved if the user presses the 'done' button on the keyboard but I would also like for the app to treat the user unfocusing
the TextField
without pressing the done button to mean the user canceled the action and the item name should be reverted to the previous name. Is there a way to call a function on unfocus only when the text was not submitted?
Asked
Active
Viewed 257 times
0
1 Answers
2
You can achieve this using the FocusNode
class in Flutter.
class RenameItemWidget extends StatefulWidget {
const RenameItemWidget({required this.initialName, this.onSave});
final String initialName;
final void Function(String value)? onSave;
@override
State<RenameItemWidget> createState() => _RenameItemWidgetState();
}
class _RenameItemWidgetState extends State<RenameItemWidget> {
late TextEditingController _textEditingController;
late FocusNode _focusNode;
late String _itemName;
@override
void initState() {
super.initState();
_textEditingController = TextEditingController(text: widget.initialName);
_focusNode = FocusNode();
_itemName = widget.initialName;
_focusNode.addListener(_onFocusChange);
}
@override
void didUpdateWidget(RenameItemWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialName != widget.initialName) {
_textEditingController.text = widget.initialName;
_itemName = widget.initialName;
}
}
@override
Widget build(BuildContext context) => TextField(
controller: _textEditingController,
focusNode: _focusNode,
decoration: const InputDecoration(labelText: 'Item name'),
onSubmitted: (_) => _saveName(),
);
void _onFocusChange() {
if (!_focusNode.hasFocus && _textEditingController.text != _itemName) {
setState(() => _textEditingController.text = _itemName);
}
}
void _saveName() {
setState(() => _itemName = _textEditingController.text);
widget.onSave?.call(_itemName);
}
@override
void dispose() {
_textEditingController.dispose();
_focusNode.dispose();
super.dispose();
}
}

Hamed
- 5,867
- 4
- 32
- 56
-
1Thanks! That was exactly was I was looking for! One thing though: It seems that when you submit, the `focusNode` listener happens before `onSave` so even though the widget was updated with the new name, the listener executed with the old `initialName` and sets the controller text to the old name. After doing a hot reload it updates properly. I fixed it by changing `widget.initalName` to `_itemName` in the listener function though you could also use `didUpdateWidget()` and set the controllet text there. – Kulpas Mar 25 '23 at 16:27