Pages on Navigator stack rebuild when a new page is pushed in Flutter

9,969

Solution 1

I solved the problem by simply changing the class like this :

import 'package:flutter/material.dart';

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

class _UserLoaderState extends State<UserLoader> {
  Widget _form; // Save the form

  @override
  Widget build(BuildContext context) {
    if (_form == null) { // Create the form if it does not exist
      _form = _createForm(context); // Build the form
    }
    return _form; // Show the form in the application
  }

  Widget _createForm(BuildContext context) {
    // This is the exact content of the build method in the question

    final _formKey = new GlobalKey<FormState>();
    final _emailController = new TextEditingController();

    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Informations"),
          actions: <Widget>[
            new IconButton(
                icon: const Icon(Icons.save),
                onPressed: () {
                  // unrelated stuff happens here
                })
          ],
        ),
        body: new Center(
          child: new SingleChildScrollView(
              child: new Form(
                  key: _formKey,
                  child: new Column(children: <Widget>[
                    new ListTile(
                      leading: const Icon(Icons.email),
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "Email",
                        ),
                        keyboardType: TextInputType.emailAddress,
                        controller: _emailController,
                        validator: _validateEmail,
                      ),
                    ),
                  ]))),
        ));
    }
  }
}

Hope this may help someone else someday.

Solution 2

All you need to do is move this row

final _formKey = new GlobalKey<FormState>();

from build method to state class declaration (i.e. outside from build). Key must be created once when class is created. In your case the key is re-created each build action.

Share:
9,969
Daneel
Author by

Daneel

Developper

Updated on December 05, 2022

Comments

  • Daneel
    Daneel over 1 year

    I'm developing a Flutter app which prompts a form asking for some personal info.

    The problem is that the page is being rebuilt every time something happens, like when the screen orientation changes or when a text field gets the focus (the keyboard appears and disappears right away, preventing the user from typing anything).

    Obviously something is triggering unwanted rebuilds, but I couldn't find out what and where.

    When I plug this page as the homepage, everything works fine. The issue happens when I plug the page at its intended position, after an animation is displayed on a splash screen, so I guess it has something to do with my problem.

    Main class :

    import 'package:flutter/material.dart';
    import './view/SplashScreen.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new SplashScreen(),
        );
      }
    }
    

    Splash screen :

    import 'package:flutter/material.dart';
    import 'dart:async';
    import './UserLoader.dart';
    
    class SplashScreen extends StatefulWidget {
      @override
      _SplashScreenState createState() => new _SplashScreenState();
    }
    
    class _SplashScreenState extends State<SplashScreen>
        with SingleTickerProviderStateMixin {
      AnimationController _iconAnimationController;
      CurvedAnimation _iconAnimation;
    
      @override
      void initState() {
        super.initState();
    
        _iconAnimationController = new AnimationController(
            vsync: this, duration: new Duration(milliseconds: 2000));
    
        _iconAnimation = new CurvedAnimation(
            parent: _iconAnimationController, curve: Curves.easeIn);
        _iconAnimation.addListener(() => this.setState(() {}));
    
        _iconAnimationController.forward();
    
        startTimeout();
      }
    
      @override
      Widget build(BuildContext context) {
        return new Material(
          color: Colors.white,
          child: new InkWell(
            child: new Center(
              child: new Container(
                width: 275.0,
                height: 275.0,
                decoration: new BoxDecoration(
                  image: new DecorationImage(
                      colorFilter: new ColorFilter.mode(
                          Colors.white.withOpacity(_iconAnimation.value),
                          BlendMode.dstATop),
                      image: new AssetImage("images/logo.png")),
                ),
              ),
            ),
          ),
        );
      }
    
      void handleTimeout() {
        Navigator.of(context).pushReplacement(new MaterialPageRoute(
            builder: (BuildContext context) => new UserLoader()));
      }
    
      startTimeout() async {
        var duration = const Duration(seconds: 3);
        return new Timer(duration, handleTimeout);
      }
    }
    

    Faulty page :

    import 'package:flutter/material.dart';
    
    class UserLoader extends StatefulWidget {
      @override
      _UserLoaderState createState() => new _UserLoaderState();
    }
    
    class _UserLoaderState extends State<UserLoader> {
      @override
      Widget build(BuildContext context) {
        final _formKey = new GlobalKey<FormState>();
        final _emailController = new TextEditingController();
    
        return new Scaffold(
            appBar: new AppBar(
              title: new Text("Informations"),
              actions: <Widget>[
                new IconButton(
                    icon: const Icon(Icons.save),
                    onPressed: () {
                      // unrelated stuff happens here
                    })
              ],
            ),
            body: new Center(
              child: new SingleChildScrollView(
                  child: new Form(
                      key: _formKey,
                      child: new Column(children: <Widget>[
                        new ListTile(
                          leading: const Icon(Icons.email),
                          title: new TextFormField(
                            decoration: new InputDecoration(
                              hintText: "Email",
                            ),
                            keyboardType: TextInputType.emailAddress,
                            controller: _emailController,
                            validator: _validateEmail,
                          ),
                        ),
                      ]))),
            ));
        }}
    

    Can anybody help me find out why the page is continuously rebuilding itself ?

  • Nato Boram
    Nato Boram over 5 years
    You should probably note the changes you did to the class so other people won't have to look trough the code to find an answer.
  • Daneel
    Daneel over 5 years
    @NatoBoram You're right, I added some comments in the code to highlight the changes :)
  • Sibin
    Sibin over 5 years
    @Daneel What happens if I need to do error handling in this screen? Like email validation.
  • Daneel
    Daneel over 5 years
    @Sibin Sorry, this project is a bit old for me, but as I recall, my _validateEmail returned null if the email was correct, or a message error (a simple string) which was displayed automatically underneath the field. The Save button had a method calling something like _formKey.currentState.validate() to check if everything was alright - if the email was wrong, the validator returned false and prevented the "save" action. Flutter handled it all nicely. Hope this helps, otherwise you can post a question on SO to get better answers.
  • Sibin
    Sibin about 5 years
    @Daneel Thanks for the reply. In my project implemented streambuilder then everything is working perfectly now .
  • putulputul
    putulputul about 5 years
    Dear, You save my life and save my time. Please take my heartiest thanks
  • emowise
    emowise over 4 years
    Checking _form==null saved my hours. Thanks)
  • Gyuri Majercsik
    Gyuri Majercsik about 4 years
    I wouldn't recommend this. Hot reload stops working if you cache the widget.
  • ff .n
    ff .n about 3 years
    I had this problem for a few days and I was looking for a solution, but my problem still persisted But your solution helped a lot,thanks a lot