Flutter TextField how to detect that the text field was unfocused but not submitted?

680 views Asked by At

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?

1

There are 1 answers

1
Hamed On BEST ANSWER

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