onEditingComplete is not called after unfocus

Viewed 2831

I have a TextField like this. The additional code is necessary to show that in different situations, I do various focus manipulation.

  final node = FocusScope.of(context);
  Function cleanInput = () => {controller.text = controller.text.trim()};
  Function onEditingComplete;
  Function onSubmitted
  TextInputAction textInputAction;
  if (!isLast) {
    onEditingComplete = () => {
          cleanInput(),
          node.nextFocus(),
        };
    onSubmitted = (_) => {cleanInput()};
    textInputAction = TextInputAction.next;
  } else {
    onEditingComplete = () => {
          cleanInput(),
        };
    onSubmitted = (_) => {
          cleanInput(),
          node.unfocus(),
        };
    textInputAction = TextInputAction.done;
  }
  Widget textInput = TextField(
      textInputAction: textInputAction,
      controller: controller,
      onEditingComplete: onEditingComplete,
      onSubmitted: onSubmitted,
      keyboardType: textInputType,
      ));

As you can see, I have functions I want to run onEditingComplete. However, this only gets called when I press the Next or Done buttons on my keyboard (or the Enter key in an emulator). If I change focus by tapping on a different field, this function does not get called.

I have tried using a Focus or FocusNode to help with this, but when I do so, the onEditingComplete function itself no longer works.

How can I get the desired effect here while everything plays nicely together?

3 Answers

Focus widget

Wrapping fields in a Focus widget might do the trick.

The Focus widget will capture focus loss events for children. With its onFocusChange argument you can call arbitrary functions.

Meanwhile, the onEditingComplete argument of TextField is unaffected and will still be called on the software keyboard "Next/Done" keypress.

This should handle field focus loss for both "Next/Done" keypress and user tapping on another field.

import 'package:flutter/material.dart';

class TextFieldFocusPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              // ↓ Add this wrapper
              Focus(
                child: TextField(
                  autofocus: true,
                  decoration: InputDecoration(
                    labelText: 'Name'
                  ),
                  textInputAction: TextInputAction.next,
                  // ↓ Handle focus change on Next / Done soft keyboard keys
                  onEditingComplete: () {
                    print('Name editing complete');
                    FocusScope.of(context).nextFocus();
                  },
                ),
                canRequestFocus: false,
                // ↓ Focus widget handler e.g. user taps elsewhere
                onFocusChange: (hasFocus) {
                  hasFocus ? print('Name GAINED focus') : print('Name LOST focus');
                },
              ),
              TextField(
                decoration: InputDecoration(
                  labelText: 'Password'
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Please add a focus node to your textfield and add a listener to your focus node to trigger when it unfocuses

   final node = FocusScope.of(context);
   node.addListener(_handleFocusChange);


  void _handleFocusChange() {
    if (node.hasFocus != _focused) {
      setState(() {
        _focused = node.hasFocus;
      });
    }
  }

    Widget textInput = TextField(
      //you missed this line of code
      focusNode: node,
      textInputAction: textInputAction,
      controller: controller,
      onEditingComplete: onEditingComplete,
      onSubmitted: onSubmitted,
      keyboardType: textInputType,
      ));

And also you can validete automatically by adding autoValidate to your code like below:

Widget textInput = TextField(
 //add this line of code to auto validate
  autoValidate: true,
  textInputAction: textInputAction,
  controller: controller,
  onEditingComplete: onEditingComplete,
  onSubmitted: onSubmitted,
  keyboardType: textInputType,
  ));
FocusNode _node;
bool _focused = false;

@override
void initState() {
      super.initState();
       _node.addListener(_handleFocusChange);
    }

  void _handleFocusChange() {
        if (_node.hasFocus != _focused) {
          setState(() {
           _focused = _node.hasFocus;
        });
      }
      }
  @override
   void dispose() {
     _node.removeListener(_handleFocusChange);
    _node.dispose();
  super.dispose();
 }

    TextFormField(
focusNode: _node)
Related