23

I have a custom StatefulWidget that when typing into an empty field, it will automatically add a new empty field below so the user can keep adding data.

However, as I use setState in onChanged to add the new field, the keyboard is dismissed and focus is lost on the currently focused field.

How can Io prevent this happening?

 TextField(
          hintText: widget.newEntryHint,
          text: data[index].value,
          onChanged: (val) {
            setState(() {
              data[index].value = val;
    
              //if last item in list, add an extra field below
              if (val.length > 0 && index == (data.length -1)) {
                data.add(TextListItem(value: ""));
              }
            });
          },
        )

Custom TextField for reference:

class MyTextField extends StatefulWidget {
  MyTextField({
    this.text,
    this.hintText = "",
    this.onChanged,
    this.onSubmitted,
    this.textAlign = TextAlign.left,
    this.focusNode,
    this.autofocus = false,
    this.obscureText = false,
    this.padding = const EdgeInsets.all(0.0),
    this.keyboardType = TextInputType.text,
    this.canEdit = true,
    this.isDarkMode = false,
    this.textCapitalization = TextCapitalization.sentences,
    this.key,
  });

  final String text;
  final String hintText;
  final ValueChanged<String> onChanged;
  final ValueChanged<String> onSubmitted;
  final TextAlign textAlign;
  final FocusNode focusNode;
  final bool autofocus;
  final bool obscureText;
  final EdgeInsets padding;
  final TextInputType keyboardType;
  final TextCapitalization textCapitalization;
  final Key key;

  final bool canEdit;

  final isDarkMode;

  @override
  _MyTextFieldState createState() => _MyTextFieldState();
}

class _MyTextFieldState extends State<MyTextField> {
  static const double textFieldPadding = 12.0;
  TextEditingController editingController;

  @override
  void initState() {
    super.initState();
    editingController = TextEditingController(text: widget.text);
  }

  @override
  Widget build(BuildContext context) {
    return IgnorePointer(
      ignoring: !widget.canEdit,
      child: Column(
        children: <Widget>[
          Padding(
            padding: EdgeInsets.only(
                top: textFieldPadding + widget.padding.top, bottom: textFieldPadding + widget.padding.bottom, left: widget.padding.left, right: widget.padding.right),
            child: TextField(
              key: widget.key,
              maxLines: null,
              textCapitalization: widget.textCapitalization,
              keyboardType: widget.keyboardType,
              keyboardAppearance: widget.isDarkMode ? Brightness.dark : Brightness.light,
              controller: editingController,
              onSubmitted: widget.onSubmitted,
              onChanged: widget.onChanged,
              style: TextStyle(
                  color: widget.isDarkMode ? Colors.white : MyColors.textBlue,
                  fontSize: 16.0,
                  fontWeight: FontWeight.w500),
              autofocus: widget.autofocus,
              focusNode: widget.focusNode,
              textAlign: widget.textAlign,
              obscureText: widget.obscureText,
              decoration: InputDecoration(
                hintText: widget.hintText,
                hintStyle: TextStyle(
                    color: widget.isDarkMode ? MyColors.black[700] : MyColors.grey,
                    fontSize: 16.0,
                    fontWeight: FontWeight.w500),
                border: InputBorder.none,
              ),
            ),
          ),
          Divider(
            color: widget.isDarkMode ? MyColors.black : MyColors.grey[150],
            height: 1.0,
          ),
        ],
      ),
    );
  }
}
Josh Kahane
  • 16,765
  • 45
  • 140
  • 253

4 Answers4

15

I have had the same problem. I have found that I use key: UniqueKey() in my container. On every setState it generates uniqueKey and updates the children. Check your keys.

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
EvaNi
  • 171
  • 1
  • 3
  • Thank you very much, I've replaced UniqueKey() with ValueKey(0) and now there is no issue with input in TextFormField, though Dissmisable widget now doesn't work as before. What did you mean with check your keys? How to solve this issue? – Jan Jun 08 '21 at 12:00
  • 2
    I had the same problem, I was defining a GlobalKey() for my Scaffold inside build function, I just moved it outside the function. – Mireille Jun 26 '21 at 18:55
  • i have same problem as mentioned by @Jan in above comment with dismissable widget? Have you found solution @Jan? – shraddha11 Jun 28 '21 at 06:20
  • shraddha11 no sorry I didn't found solution, instead I've made workaround, removed setState((){}) from onChanged parameter in TextFormField and then don't have this issue – Jan Jul 07 '21 at 08:16
  • 1
    @Jan @shraddha11 You will have to create a `ValueKey` based on a final id. For instance `key: ValueKey(task.id)` (inside your `Dismissible`). – genericUser Jan 03 '22 at 11:14
  • I'm using a `TextFormField` inside a `Dismissible` widget. Although it solves the keyboard issue, now I'm facing an issue with the text cursor position. I'm using a setState on each text change, and each time the pointer jumps to the beginning of the text. How do I solve this? – genericUser Jan 03 '22 at 11:24
3

In my case , that issue was about the key of TextFormField.

I created a formkey at outside of the page

final _formKey = GlobalKey<FormState>();

and than I gave that key to textformfield as a parameter

 TextFormField(
              focusNode: textFormFieldFocusNode,
              key: _formKey,
              ....
              ....

I hope it creates a perspective to that kind problems.

1

I know it's been a while, maybe something was fixed since then, but I'm not seeing your issue anymore. At least with the way I'm setting up your widget (slight difference between your data.add() vs my fields.add()).

List<Widget> _fields;
ScrollController controller = new ScrollController();

_buildData() {
  _fields = new List();
  for (int i = 0; i < 3; i++) {
    _fields.add(_createMyTextField('hello$i', i));
  }
}

Widget _createMyTextField(String text, int index) {
  return MyTextField(
    text: text,
    hintText: 'hello hint$index',
    onChanged: (val) {
      setState(() {
        //if last item in list, add an extra field below
        if (val.length > 0 && index == (_fields.length-1)) {
          _fields.add(_createMyTextField("", index+1));
        }
      });
    },
  );
}

@override
void initState() {
  super.initState();
  _buildData();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text(widget.title)),
    body: ListView(
      controller: controller,
      children: _fields.toList()
    )
  );
}

enter image description here

TWL
  • 6,228
  • 29
  • 65
0

I recently had the same issue. The problem in my case was caused by a Container in the tree having an optional margin that was controlled by the focus within the child tree. When a TextFormField was tapped the keyboard would pop up and down and the field did not have focus. However, the container's margin was correctly changed due to the focus.

By changing the container to have a margin of 0 instead of null, it fixed the problem. It may also apply to other properties like padding etc.

zkon
  • 568
  • 1
  • 6
  • 19