Why does my widget rebuild when I use keyboard

Viewed 3585

I have this issue of rebuilding widget when The keyboards shows up. I tried to use the sizer package but never could figure out how to get it to work when I go back from this screen everything in the previous screen will rebuild, Please note: If I don't click on the typeaheadwidget such that the keyboard doesn't show up the state is preserved in the previous screen but as soon as the keyboard pops up the widgets get rebuilt Could you please check ?

class SearchScreen extends StatefulWidget {
  @override
  _SearchScreenState createState() => _SearchScreenState();
}

class _SearchScreenState extends State<SearchScreen> {
  TextEditingController pickUpTextEditingController = TextEditingController();
  TextEditingController dropOffTextEditingController = TextEditingController();

  @override
  void initState() {
    super.initState();
  }

  @override
  @mustCallSuper
  Widget build(BuildContext context) {
    String placeAddress =
        Provider.of<AppData>(context).pickUpLocation.placeName ?? "";
    pickUpTextEditingController.text = placeAddress;

    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: Stack(
        children: [
          Container(
            height: 250.0,
            decoration: BoxDecoration(
              color: Colors.white,
              boxShadow: [
                BoxShadow(
                  color: Colors.black,
                  blurRadius: 6.0,
                  spreadRadius: 0.5,
                  offset: Offset(0.7, 0.7),
                )
              ],
            ),
            child: Padding(
              padding: EdgeInsets.only(
                  left: 25.0, top: 30.0, right: 25.0, bottom: 20.0),
              child: Column(
                children: [
                  SizedBox(height: 5.0),
                  Stack(
                    children: [
                      GestureDetector(
                          onTap: () {
                            Navigator.pop(
                                //send back data
                                context,
                                dropOffTextEditingController.text);
                          },
                          child: Icon(Icons.arrow_back)),
                      Center(
                        child: Text(
                          "Set Drop Off",
                          style: TextStyle(
                              fontSize: 18.0, fontFamily: "Brand-Bold"),
                        ),
                      )
                    ],
                  ),
                  SizedBox(height: 16.0),
                  Row(
                    children: [
                      Image.asset("images/images/pickicon.png",
                          height: 16.0, width: 16.0),
                      SizedBox(width: 18.0),
                      Expanded(
                          child: Container(
                        decoration: BoxDecoration(
                          color: Colors.grey[400],
                          borderRadius: BorderRadius.circular(5.0),
                        ),
                        child: Padding(
                          padding: EdgeInsets.all(3.0),
                          child: TextField(
                            controller: pickUpTextEditingController,
                            decoration: InputDecoration(
                              hintText: "PickUp Location",
                              fillColor: Colors.grey[400],
                              filled: true,
                              border: InputBorder.none,
                              isDense: true,
                              contentPadding: EdgeInsets.only(
                                  left: 11.0, top: 8.0, bottom: 8.0),
                            ),
                          ),
                        ),
                      ))
                    ],
                  ),
                  SizedBox(height: 10.0),
                  Row(
                    children: [
                      Image.asset("images/images/desticon.png",
                          height: 16.0, width: 16.0),
                      SizedBox(width: 18.0),
                      Expanded(
                        child: Container(
                          decoration: BoxDecoration(
                            color: Colors.grey[400],
                            borderRadius: BorderRadius.circular(5.0),
                          ),
                          child: Padding(
                            padding: EdgeInsets.all(3.0),
                            child: TypeAheadField(
                              itemBuilder: null,
                              onSuggestionSelected: null,
                              suggestionsCallback: null,
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
4 Answers

You should not try to control when the build method is called. Flutter will call build when it decides it needs to (e.g. keyboard appears, device rotated, parent rebuilds, etc).

Instead, you should make sure that your build method is a "pure" function. In Flutter specifically, this means that you should not perform any action with "side-effects" (basically anything which modifies the state of the app).

For example:

Widget build(BuildContext context) {
  final x = 2 + 3;  // fine, nothing else is modified
  final state = context.watch<MyModel>();  // also fine, only reading data
  controller.text = "hello";  // BAD, modifies the state of the app

  return ...;
}

Instead, you should move your logic with side effects into other lifecycle methods (e.g. initState(), didChangeDepencencies(), etc).

For example, if you want to set your text field to a particular string when it first appears, you can use initState:

class _SearchScreenState extends State<SearchScreen> {
  @override
  void initState() {
    super.initState();
    final data = context.read<AppData>();
    controller.text = data.pickUpLocation.placeName ?? "";
  }

  Widget build(BuildContext context) {
    // ...
  }
}

Now build() can be called whenever it has to be, without resetting the state of your text field.

Note that, even if there was some way to prevent your widget from being rebuilt, this is also likely not what you want, since the UI would not update to accommodate the keyboard.

the only reason why your widgets got rebuilds after keyboard pop up. is that one or more of your widgets size depends on MediaQuery.

you can try to ge your screen size from LayoutBuilder as an alternative for MediaQuery.

When you tap the TextField widget, it makes the keyboard show up. And when the keyboard shows up, your screen size changes. This causes the rebuild

Give the textfield an initial value like this:

initvalue:Provider.of<AppData>(context).pickUpLocation.placeName ?? ""

and use onchange method in text field instead of text editing controller like this:

onchange(value){
Provider.of<AppData>(context).pickUpLocation.placeName=value;}
Related