15

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!

Sukhi
  • 13,261
  • 7
  • 36
  • 53
Mohammad Shahhoud
  • 469
  • 3
  • 6
  • 16
  • similar issue to this, https://stackoverflow.com/questions/57409531/how-to-avoid-full-page-reloading-when-keyboard-pops-up/57410479#57410479 also add 'resizeToAvoidBottomInset: false' to your Scaffold – F-1 Aug 09 '19 at 10:14
  • @Mohammad : I am also facing this problem. Did you get any solution for it? – Jatin Aug 11 '19 at 17:32
  • @Jatin Please check my answer below. – Mohammad Shahhoud Aug 15 '19 at 08:49

4 Answers4

3

Keep in mind you should always write your build methods as if they're being called 60 times a second. So they should be (a) fast and (b) idempotent.

Randal Schwartz
  • 39,428
  • 4
  • 43
  • 70
2

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'),
  ),
Mazin Ibrahim
  • 7,433
  • 2
  • 33
  • 40
  • That didn't solve the problem, the keyboard will call build anyway in the ```step3``` and didn't call build in ```step2``` which I didn't understand why! – Mohammad Shahhoud Aug 11 '19 at 09:36
  • Your question was about the rebuilds caused by navigation events, but I also pointed you to the post discussing the same issue caused by keyboard events. Which, according to the post can be solved using a similar technique, thanks. – Mazin Ibrahim Aug 15 '19 at 16:52
2

Based on Mazin Ibrahim answer, I managed to solve the keyboard build problem:

Define all your navigator widgets as final in case using routes.

In case of using routes, I managed to define all my navigation widgets as final fields inside the routes class.

That's stops of recreating the same widget more than once, especially that my widgets are all Stateless.

Mohammad Shahhoud
  • 469
  • 3
  • 6
  • 16
1

Wrap Scaffold in a Consumer widget or where it need.

mario francois
  • 1,291
  • 1
  • 9
  • 16