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!
To make the keyboard go away itself, we need to remove “focus” from all of our text fields (if your app has more than one text field) by calling the unfocus() method: FocusManager.
The build method is called any time you call setState , your widget's dependencies update, or any of the parent widgets are rebuilt (when setState is called inside of those).
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.
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'),
),
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With