17

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 !

Nadox56
  • 355
  • 1
  • 2
  • 8

8 Answers8

24

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.

dshukertjr
  • 15,244
  • 11
  • 57
  • 94
rbp
  • 43,594
  • 3
  • 38
  • 31
  • 5
    Great work around but how would you deal when the build of the child uses a FutureBuilder ? thanks – boeledi Aug 01 '18 at 00:03
  • For me the issue was on android, iOS was working fine. (Might be iOS is handling the memory management well with ARC) The solution given here worked to get rid off the described issue on android. +10 :) – iAkshay Dec 26 '19 at 19:46
1

you just need make a new class and import that on your target class that seen problem. for example :

I usually create a class like this :

class MiddleWare
{
  static MiddleWare shared = MiddleWare();
  _MiddleWare(){}
  String myText = "my Text";
  // every variables should be here...
}

and

import "MiddleWare.dart";

class myclass extends StatefulWidget {
  @override
  _myclassState createState() => _myclassState();
}

class _myclassState extends State<myclass> {
  @override
  Widget build(BuildContext context) {
    return Container(child: Text(MiddleWare.shared.myText));
  }
}

that's it.

1

hi dont use Scaffold key i.e

    Scaffold (
    ...
    key: _scaffoldKey, //remove this
    ...
    )

on the page and do a complete page rebuild (not hot reload), and you should be fine worked for me tho!

The Billionaire Guy
  • 3,382
  • 28
  • 31
  • I removed all unnecessary Keys/UniqueKeys and it worked, only removing the scaffoldKey didn't work for me, but it helped me test removing other ones, thanks for that! – Daniel Silva Sep 30 '20 at 18:32
1

In my case, I have two stateful widgets, the parent and the child. I used the pushReplacement method on the parent to fix the widget reload issue when the text form field is selected in the child widget.

Navigator.pushReplacement(
   context,
   MaterialPageRoute(builder: (context) => WidgetChildren(idUser: 
   widget.idUser)),
);
double-beep
  • 5,031
  • 17
  • 33
  • 41
zannier
  • 21
  • 1
  • 10
0

try to create a function which receives context like this

class YourPage extends StatefulWidget {
  const YourPage(Key key) : super(key: key);

  static Future<void> show({ BuildContext context,}) async {
     await Navigator.of(context, rootNavigator: true).push(
         MaterialPageRoute(
    builder: (context) => YourPage()
    );}

   @override
   _YourPageState createState() => _YourPageState();
}
......YourPage Build.....

then provide context to your page, when rebuilding it will have core context that prevents parent rebuild.

onPressed: () async {
             await YourPage.show(context: context);
0

Move your variables (controllers and keys) from build to class-fields level.

Nickolay Savchenko
  • 1,474
  • 16
  • 28
0

formkey should be final and outside the build method

Crucialjun
  • 193
  • 1
  • 8
-1

in my case it was related to this property in Scaffold widget: 'resizeToAvoidBottomInset'

I changed it to true and problem solved.