Flutter TextFormField reloads current screen when focused
Asked Answered
P

7

19

I have a TextFormField that reloads the current screen when I tap on it to enter text. When I tap on the formfield the software keyboard is displayed briefly before the entire screen reloads and renders all the widgets again. I am running the app on an Android device.

Container(
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              TextFormField(
                validator: (value) {
                  if (value.isEmpty) {
                    return 'Your input cannot be empty';
                  }
                },
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: RaisedButton(
                  onPressed: () {

                    if (_formKey.currentState.validate()) {
                      print('validated');
                    }
                  },
                  child: Text('Save'),
                ),
              ),
            ],
          ),
        ),
        margin: EdgeInsets.only(top:8.0),
  ),
Pressey answered 6/8, 2018 at 1:27 Comment(3)
Where did you declare the _formKey var?Torrent
I declared the _formKey var above in the same class as a class property/variable.Pressey
@Amit You need to make your build method pure .. keyboard opening & closing can call the build method again and hence rendering your widgets.Combo
C
2

The problem is that the controller of the TextFormField is rebuild when you click on the field, and that's the reason of your issue.

So to solve that, did you try to create a Statefull widget and then creating a TextEditingController in the State of this widget and passing it as an argument to the TextFormField ?

Clemmy answered 4/6, 2019 at 19:4 Comment(3)
This solved it for me. Just convert to stateful widget and make sure that the TextEditingController is created in the state. Although I am not 100% I understand why the TextEditingController gets rebuilt and reinitialized to its original value... making it the widget stateful fixes it, thanksPreprandial
That’s because a Stateless widget will be completely re-build whenever its parent call the build method. When I say completely I mean that the whole object is re-instantiated and by doing so, all its fields are re-created... it’s also true for the StatefullWidget class but not for its « child » class, the State class in which I you actually declare your build method which does not get re-instantiated! You can find great documentation, articles and videos on the web for that matter !Clemmy
That's helpful clarification. I will look it up further, thanks!Preprandial
H
1

I had the same Problem. this was my code

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  Model model = Model();

  @override
  Widget build(BuildContext context) {
  GlobalKey<FormState> _formKey = GlobalKey<FormState>();
    var mediaWidth = MediaQuery.of(context).size.width / 2.0;
    return Scaffold(
...

and I solved this problem by declaring the _formKey outside of build method. and this worked for me.

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  Model model = Model();
  GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    var mediaWidth = MediaQuery.of(context).size.width / 2.0;
    return Scaffold(
...

hope it will help you

Height answered 4/3, 2020 at 17:56 Comment(0)
A
0

Yes, that happens because when the keyboard appears, the flutter scaffold gets resize to the current available screen size. So, we can easily handle this by preventing the scaffold size change. I suggest to set scaffold resizeToAvoidBottomInset property false. If it's true the body and the scaffolds floating widgets should size themselves to avoid the onscreen keyboard whose height is defined by the ambient MediaQuery's, MediaQueryData,viewInsets bottom property.

Solution:

resizeToAvoidBottomInset: false,

Complete example:

@override
Widget build(BuildContext context) {
  setDisplayData();
  return Scaffold(
    resizeToAvoidBottomInset: false,
    appBar: getAppBar(),
    body: OrientationBuilder(
      builder: (context, orientation) {
        return orientation == Orientation.portrait
            ? _buildVerticalLayout()
            : _buildHorizontalLayout();
      },
    ),
  );
Actinopod answered 29/4, 2020 at 6:1 Comment(0)
C
0

Check if you are using MediaQueries wrongly in your project, I had similar issue and it stopped when I changed the MediaQuery in my case:

Size _size = MediaQuery.of(context).size;

removing this piece of code fixed my app.

Chromatic answered 10/9, 2021 at 11:36 Comment(0)
S
0

When TextFormField focused the size of screen will changed because of the appearance of keyboard, that cause rebuild of state, you cant prevent re-build of state.

Instead of trying prevent re-build state, you need to solve problems which happen when state do re-build, one of common problem is declaration and initialization variables inside build(BuildContext context){ ... }' function.

The main problem, when you need to get some data related of context (like size of screen), in this case I prefer to pass this value from parent Widget...

For example this code will cause problem when re-build state:

  @override
  Widget build(BuildContext context) {
    double? _screenHeight =  MediaQuery.of(context).size.height;
    return Container();
  }

To solve problem get _screenHeight from parent, to know how to do that look at https://mcmap.net/q/92766/-passing-data-to-statefulwidget-and-accessing-it-in-its-state-in-flutter

Sprinkle answered 11/1, 2022 at 1:40 Comment(0)
O
0

Any chance your widget is somewhere inside a StreamBuilder? I was having the exact same problem and it turned out to be a StreamBuilder issue.

Say you have a function getStream() which returns a string. For now we'll call your widget with the container MyWidget(). Here would be the faulty way of building this:

@override
Widget build(BuildContext context) {
        return StreamBuilder(
            stream: getStream(),
            builder: (context, snapshot) {
                //your logic here
                return MyWidget();
            },
        );
}

The issue here is that every time something on the screen has to change, build is going to be called again. In the code above, calling build calls getStream() again, which creates a new stream instead of using the one that was already there.

When you tap on a text field and it's focused on, build is called. When build is called, a new stream is made. When this new stream is created, widgets returned by StreamBuilder are also re-built (in this case, MyWidget()). And when MyWidget() is rebuilt, so is the text field inside of it, which starts out unfocused.

So? How do we fix this? First, you wanna make sure the widget returning StreamBuilder is stateful (we're gonna refer to this one as RootWidget()). Second, you want to declare a variable with your stream inside the state. It would look something like this:

class RootWidgetState extends State<RootWidget> {

    final Future<E> _myStream= getStream();

    @override
    Widget build(BuildContext context) {
            return StreamBuilder(
                stream: _myStream,
                builder: (context, snapshot) {
                    //your logic here
                    return MyWidget();
                },
            );
    }
}

Where E just represents what kind of stream you're getting. If for some reason you can't access getStream() in the initializer, replace final with late, and initialize the variable in initState().

I also asked about this. Here's my question thread.

Outlast answered 30/1 at 19:40 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Bik
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewChur
R
-1

This problem can be anything. Code structure could be one.

In my case I had a FutureBuilder with a auth provider in my root widget. So when the keyboard resizes the screen it would rebuild that specific FutureBuilder. Why? I don't know.

The solution for me was to move that specific that provider into initState. In the end the text controller focus was working correctly.

Lesson: check above state or builders or providers and refactor some code.

Ranger answered 27/3 at 7:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.