Flutter – Keyboard Show and Hide Causes Build Calls

dartflutter

I've been recently learning Flutter and came across some strange problem, when I click on the TextField the keyboard will be shown and the build will be called for the hosted and all ancestor widgets.

I've read that when TextField clicked, the build method will be called, then the whole widget will be rebuilt, but the case that I encountered is somehow strange, since the build is not called on the first opened widget.

Scenario:

1 – Open app.

2 – First widget loads and printed on screen: "First build called", and when I click on the TextField, the keyboard pops up and nothing printed on console.

3 – Click on "Open Second", the second widget loads and printed on screen:
"Second build called", and when I click on the TextField, the keyboard pops up and print: "Second build called" (in step[1] nothing printed!).

4 – Click on "Open First", the first widget loads again and printed on screen: "First build called", and when I click on the TextField, the keyboard pops up and print: "Second build called First build called" (in step[1] for the same widget that didn't print anything!).

Code:

void main() {
  runApp(MaterialApp(home: First()));
}

class First extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('First build called');
    return Scaffold(
      appBar: AppBar(
        title: Text("HI Ramadan"),
      ),
      body: Column(
        children: <Widget>[
          TextField(),
          RaisedButton(onPressed: () {
            Navigator.push(
                context, MaterialPageRoute(builder: (context) => Second()));
          }, child: Text('Open Second'),),
        ],
      ),
    );
  }
}

class Second extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Second build called');
    return Scaffold(
      body: Column(
        children: <Widget>[
          TextField(),
          RaisedButton(onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) => First()));
          }, child: Text('Open First')),
        ],
      ),
    );
  }
}

Flutter doctor:

[√] Flutter (Channel stable, v1.7.8+hotfix.4, on Microsoft Windows [Version 10.0.17134.885], locale en-US)
    • Flutter version 1.7.8+hotfix.4 at C:\flutter
    • Framework revision 20e59316b8 (3 weeks ago), 2019-07-18 20:04:33 -0700
    • Engine revision fee001c93f
    • Dart version 2.4.0

[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at C:\Users\ASUS\AppData\Local\Android\sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1248-b01)
    • All Android licenses accepted.

[√] Android Studio (version 3.3)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 34.0.1
    • Dart plugin version 182.5215
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1248-b01)

[√] VS Code (version 1.36.1)
    • VS Code at C:\Users\ASUS\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.2.0

[√] Connected device (1 available)
    • FLA LX1 • 75U7N18410006702 • android-arm64 • Android 9 (API 28)

• No issues found!


Questions:

1 – Why this is happening (Called build on every widget that is not the root of the tree)?

2 – Is there a way to force flutter not calling build when the keyboard shows or hides? since I have a form, when I click over TextField and typing something, then clicking over DropDown the DropDown will be shown then hide directly (because build called) and I need to click on the DropDown one more time to select!

Best Answer

First is rebuilt because its position in the navigation stack changed.

As for your keyboard issue, this post tackled it, you just have to make a slight change to your code:

Change this :

  RaisedButton(
      onPressed: () {
         Navigator.push(context, MaterialPageRoute(builder: (context) => Second()));
      },
      child: Text('Open Second'),
  ),

  RaisedButton(
      onPressed: () {
         Navigator.push(context, MaterialPageRoute(builder: (context) => First()));
      },
      child: Text('Open First'),
  ),

To:

  RaisedButton(
      onPressed: () {
          final page = Second();
          Navigator.push(context,MaterialPageRoute(builder: (context) => page ));
      }, 
      child: Text('Open Second'),
  ),

  RaisedButton(
      onPressed: () {
          final page = First();
          Navigator.push(context,MaterialPageRoute(builder: (context) => page ));
      }, 
      child: Text('Open First'),
  ),
Related Topic