157
votes

I have a Column of Expanded widgets like this:

 return new Container(
      child: new Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          new Expanded(
            flex: 1,
            child: convertFrom,
          ),
          new Expanded(
            flex: 1,
            child: convertTo,
          ),
          new Expanded(
            flex: 1,
            child: description,
          ),
        ],
      ),
    );

It looks like this:

enter image description here

convertFrom, includes a TextField. When I tap on this text field, the Android keyboard appears on the screen. This changes the screen size, so the widgets resize like this:

enter image description here

Is there a way to have the keyboard "overlay" the screen so that my Column doesn't resize? If I don't use Expanded widgets and hardcode a height for each widget, the widgets don't resize, but I get the black-and-yellow striped error when the keyboard appears (because there isn't enough space). This also isn't flexible for all screen sizes.

I'm not sure if this is an Android-specific or Flutter-specific.

14

14 Answers

386
votes

Updated Answer

resizeToAvoidBottomPadding is now deprecated.

The updated solution is to set resizeToAvoidBottomInset property to false.


Original Answer

In your Scaffold, set resizeToAvoidBottomPadding property to false.

54
votes

Most other answers suggest using resizeToAvoidBottomPadding=false. In my experience this allows the keyboard to cover up text fields if they are underneath where the keyboard would appear.

My current solution is to force my column to be the same height as the screen, then place it in a SingleChildScrollView so that Flutter automatically scrolls my screen up just enough when the keyboard is used.

Widget build(BuildContext context) {
  return Scaffold(
    body: SingleChildScrollView(
      physics: NeverScrollableScrollPhysics(),
      child: ConstrainedBox(
        constraints: BoxConstraints(
          minWidth: MediaQuery.of(context).size.width,
          minHeight: MediaQuery.of(context).size.height,
        ),
        child: IntrinsicHeight(
          child: Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              // CONTENT HERE
            ],
          ),
        ),
      ),
    ),
  );
}

I use NeverScrollableScrollPhysics so that the user cannot scroll around themselves.

24
votes

Set resizeToAvoidBottomInset to false instead of resizeToAvoidBottomPadding which is deprecated.

    return Scaffold(
      resizeToAvoidBottomInset : false,
      body: YourWidgets(),
    );
12
votes

My approach is to use SingleChildScrollView with the ClampingScrollPhysics physics.

SingleChildScrollView(
  physics: ClampingScrollPhysics(),
  child: Container(),
)
6
votes

My suggestion is to use resizeToAvoidBottomInset: false anyway to prevent widgets from resizing if the keyboard suddenly appears on the screen. For example, if a user uses Facebook chat heads while in your app.

To prevent the keyboard from overlaying widgets, on screens where you need it, I suggest the following approach, where is the height of SingleChildScrollView reduced to the height of the available space. In this case, SingleChildScrollView also scrolls to the focused widget.

final double screenHeight = MediaQuery.of(context).size.height;
final double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
  resizeToAvoidBottomInset: false,
  body: SizedBox(
    height: screenHeight - keyboardHeight,
    child: SingleChildScrollView(
      child: Column(
        children: [
          const SizedBox(height: 200),
          for (var i = 0; i < 10; i++) const TextField()
        ],
      ),
    ),
  ),
);
5
votes

Method 1: Remove android:windowSoftInputMode="adjustResize" from AndroidManifest.xml file (Otherwise it will override flutter code) and add resizeToAvoidBottomPadding: false in Scaffold like below:

Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: AppBar()
)

Method 2(Not Recommended): Just Add android:windowSoftInputMode="stateVisible" in android AndroidManifest.xml in activity it will only work for Android an Not for IOS like.

<activity
       ...
        android:windowSoftInputMode="stateVisible">

Note: Don't set it to android:windowSoftInputMode="adjustResize"

5
votes

The best solution to avoid resizing widgets and also focus on the text field is to use SingleChildScrollView() with ClampingScrollPhysics() physics. Also, remember to set height for its child (ex: use container()), so you can use your widgets through Column():

return Scaffold(
   body: SingleChildScrollView(
      physics: ClampingScrollPhysics(),
      child: Container(
         height: size.height,
         child: Column(
            children:[
               TextFormField()
            ],
         ),
      ),
   ),
);
4
votes

Well I think if we implement @Aman's solution it will make our app behaves ugly as when the keyboard appears, it will not adjust our viewport of the screen as per available height and it will make out other fields hide behind the keyboard. So I would suggest useSingleChildScrollView instead.

Wrap your code with SingleChildScrollView as given below,

 return new Container(
  child: SingleChildScrollView(
    child: new Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: <Widget>[
      new Expanded(
        flex: 1,
        child: convertFrom,
      ),
      new Expanded(
        flex: 1,
        child: convertTo,
      ),
      new Expanded(
        flex: 1,
        child: description,
      ),
    ],
  ),
 ),
);
2
votes

Depending on the use case you could also consider using a listview. That would ensure that the contents scroll when there is not enough room. As an example, you can look at the textfield demo in the gallery app

2
votes

This is the perfect solution that gives you the ability to have a full-screen column inside of a SingleChildScrollView. This allows you to create a perfect layout for all screen sizes + the ability to have a scrollable screen that only scrolls if you open the keyboard or if the screen overflows after rendering (e.g. text input field validation)

class PerfectFullScreen extends StatelessWidget {
  const PerfectFullScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
      backgroundColor: Theme.of(context).backgroundColor,
      appBar: AppBar(),
      body: Builder(
        builder: (context) => SingleChildScrollView(
            child: ConstrainedBox(
                constraints: BoxConstraints(
                    minHeight: MediaQuery.of(context).size.height -
                        (MediaQuery.of(context).padding.top + kToolbarHeight)),
                child: IntrinsicHeight(
                    child: Column(
                  children: [
                    Container(
                      height: randomImageHeight,
                      child: Image.asset(
                        "assets/images/change_password.png",
                        fit: BoxFit.cover,
                      ),
                    ),
                    Expanded(
                        child: WidgetThatShouldTakeRemainingSpace() )
                  ],
                )))),
      ),
    ));
  }
}

The important part is the ConstrainedBox with the correct BoxConstraints and the InstrinsicHeight Widget.

PS: (MediaQuery.of(context).padding.top + kToolbarHeight) == Height of the Appbar

2
votes

Might Be too late to answer but the following worked for me

Scaffold(
  body: SingleChildScrollView(
    physics: ClampingScrollPhysics(parent: NeverScrollableScrollPhysics()),
      child: Container(

Clamping will auto scroll to make textfield visible, its parent NeverScrollable will not allow the user to scroll.

2
votes

Setting the value false for resizeToAvoidBottomInset worked fine for me.

Also, resizeToAvoidBottomPadding worked fine for me. You can use either one.

1
votes

For me changing below item property from true to false

<item name="android:windowFullscreen">false</item>

in file

android/app/src/main/res/values/styles.xml

has made Flutter drag all page content upwards on input focus

Flutter drag all page content upwards on input focus

1
votes

I was facing the same issue and I started to try random solutions to fix it and suddenly this fixed it. enter image description here

Wrap the main parent container within a SingleChildScrollView() and give it the device height i.e device_height = MediaQuery.of(context).size.height.

This will make the entire page scrollable but does not resize any widget.