Flutter TextFormFields clear when dismissing keyboard

Viewed 1577

Pretty much what I describe in the title. I have a pile of TextFormFields populated from Firebase when the app launches.

The user should be able to update these, and when they are done, click a submit button to update the database. The database code all works, however there is some bug which works as follows:

TextFormField1:   "New Text Entered"
TextFormField2:   "Some more text"
TextFormField3:   "Another update here"

Now we get to a point where we need to dismiss the keyboard, so that we can see the submit button underneath. As soon as you click the little down arrow to dismiss the keyboard, all the changes above revert back to their original state.

Anyone seen this?

I am prepopulating the data in these fields at runtime, and you can edit and update the text, and it all works fine... except if you minimise the keyboard.

Please tell me that Flutter isn't doing something fundamentally stupid like reloading the widget underneath from scratch every time you ask the keyboard to go away...... It sort of feels like it is.

2 Answers

Yes. It happens to me all the time. It is because the screen rebuilds when the bottom insets (due to keyboard) changes.

  1. Enclose the TextFormField(s) inside a Form and give it a global key.
  2. Use a local variable to store the value of the TextFormField. Update it in onChanged method. All done!

I shall attach a code for easiness.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginScreen(),
    );
  }
}

// Login Screen
class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
  static GlobalKey<FormState> _loginScreenFormKey = GlobalKey<FormState>();
}

class _LoginScreenState extends State<LoginScreen> {
  String username;
  String password;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Form(
          key: LoginScreen._loginScreenFormKey,
          child: Column(
            children: [
              TextFormField(
                decoration: InputDecoration(
                  hintText: 'Enter username',
                ),
                onChanged: (value) {
                  setState(() {
                    username = value;
                  });
                },
              ),
              TextFormField(
                decoration: InputDecoration(
                  hintText: 'Enter username',
                ),
                onChanged: (value) {
                  setState(() {
                    password = value;
                  });
                },
                obscureText: true,
              ),
              RaisedButton(
                onPressed: () {
                  LoginScreen._loginScreenFormKey.currentState.save();
                },
                child: Text('submit'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

The class that includes those TextFormFields should extends State of StatefulWidget, the local state will be cleared if the dismiss of keyboard causes those fields re-render, hence you need StatefulWidget to save the local state so that it won't be re-rendered

Convert you StatelessWidget to StatefulWidget.

Related