Search code examples
formsdarttextfieldflutter

Widget rebuild after TextField selection Flutter


I'm developping a Flutter App that needed to have a form. So when the user open the app, a Splash Screen appear before the form that have the following code :

import 'package:flutter/material.dart';
import '../model/User.dart';
import './FileManager.dart';
import './MyListPage.dart';

class UserLoader extends StatefulWidget {
  @override
  _UserLoaderState createState() => new _UserLoaderState();
}

class _UserLoaderState extends State<UserLoader> {
  final userFileName = "user_infos.txt";
  User _user;

  @override
  Widget build(BuildContext context) {
    print("build UserLoader");
    final _formKey = new GlobalKey<FormState>();
    final _firstNameController = new TextEditingController();
    final _lastNameController = new TextEditingController();
    final _emailController = new TextEditingController();
    final _phoneController = new TextEditingController();

    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Informations"),
          actions: <Widget>[
            new IconButton(
                icon: const Icon(Icons.save),
                onPressed: () {
                  _user = _onFormValidate(
                      _formKey.currentState,
                      _firstNameController.text,
                      _lastNameController.text,
                      _emailController.text,
                      _phoneController.text);
                })
          ],
        ),
        body: new Center(
          child: new SingleChildScrollView(
              child: new Form(
                  key: _formKey,
                  child: new Column(children: <Widget>[
                    new ListTile(
                      leading: const Icon(Icons.person),
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "Prénom",
                        ),
                        keyboardType: TextInputType.text,
                        controller: _firstNameController,
                        validator: _validateName,
                      ),
                    ),
                    new ListTile(
                      leading: const Icon(Icons.person),
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "Nom",
                        ),
                        keyboardType: TextInputType.text,
                        controller: _lastNameController,
                        validator: _validateName,
                      ),
                    ),
Etc, etc ...

However when i tap the TextField, the keyboard appear and close immediately and all the component is rebuild. So it is impossible for me to complete the form..

Can someone have a solution please? Thanks in advance !


Solution

  • You haven't given us the entire code for this, so I don't know what the context is.

    One pitfall I myself have fallen into (and might be affecting you, as I gather from your description) is having a stateful widget nested inside another stateful widget.

    For instance,

    class Parent extends StatefulWidget {
      @override
      ParentState createState() => ParentState();
     
      (...)
    }
    
    class ParentState extends State<Parent> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Child(),
        );
      }
    
      (...)
    }
    
    class Child extends StatefulWidget {
      @override
      ChildState createState() => ChildState();
     
      (...)
    }
    
    class ChildState extends State<Child> {
      @override
      Widget build(BuildContext context) {
        return TextField(...);
      }
    
      (...)
    }
    

    The problem here is that a rebuild of Parent means that ParentState().build() is run, and a new Child instance is created, with a new ChildState object. Which resets everything.

    Try not recreating ChildWidget, but instead saving it on ParentState, like so:

    class Parent extends StatefulWidget {
      @override
      ParentState createState() => ParentState();
     
      (...)
    }
    
    class ParentState extends State<Parent> {
      Child _child;
    
      @override
      void initState() {
        _child = Child();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: _child,
        );
      }
    
      (...)
    }
    // The rest remains the same
    

    Edit: You just need to remember that, if your widget tree is a bit more complex, you may need to 1) pass a callback from the Parent to notify of state changes, and 2) not forget to also call setState() on the Child.