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?
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();
}
}