274

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.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
Mary
  • 18,347
  • 23
  • 59
  • 76

22 Answers22

624

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.

Swift
  • 3,250
  • 1
  • 19
  • 35
Shady Aziza
  • 50,824
  • 20
  • 115
  • 113
  • 24
    Is there any way to produce a similar effect to `android:windowSoftInputMode="adjustPan"`? If I add `resizeToAvoidBottomPadding` to the scaffold it ends up covering the TextField. – Nathan Bird Mar 09 '18 at 06:55
  • 4
    I do not know about android, but it is generally a good practice to wrap your layout inside a ListView in this case – Shady Aziza Jun 04 '18 at 08:12
  • @KishanVyas same with me image is gone. – stuckedunderflow Dec 28 '18 at 06:10
  • 4
    `resizeToAvoidBottomPadding` is deprecated, use `resizeToAvoidBottomInset` [check this Answer](https://stackoverflow.com/a/56095293/5212904) – ArtiomLK Jun 17 '19 at 17:54
  • 2
    This does nothing of the sort suggested in the question. All widgets underneath the keyboard still resize. – Bisclavret May 26 '20 at 13:24
  • ```resizeToAvoidBottomPadding``` removes the default functionality to keep UI above the keyboard so if you set it false then UI will be covered by the keyboard checkout solution here https://stackoverflow.com/a/62245570/9955978 – Shubham Sharma Jun 07 '20 at 12:52
  • it works when I adding "resizeToAvoidBottomInset: false," – aravinth C Mar 22 '23 at 05:35
121

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: const 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.

Vinoth Vino
  • 9,166
  • 3
  • 66
  • 70
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • 10
    I tried your way without `NeverScrollableScrollPhysics` and all looks fine except for user can try to swipe up and down and they can see the overscroll glow. When I use the `NeverScrollableScrollPhysics` it gives me the same behavior as `resizeToAvoidBottomInset` – Sajad Jaward Jun 07 '20 at 05:46
  • Set the primary property to false. – VLXU Aug 02 '20 at 16:10
  • 12
    but using `NeverScrollableScrollPhysics` makes it so it doesn't get scrolled up with the keyboard too. – hewa jalal Dec 09 '20 at 12:42
  • 10
    remove, NeverScrollableScrollPhysics() line of the SingleChildScrollView Widget, or replace ClampingScrollPhysics() to there. then it will scroll up with the keyboard. – Supun Dewapriya Jan 29 '21 at 17:07
  • 2
    I had the same behavior with or without the IntrinsicHeight Widget. The documentation says to avoid using it: "This class is relatively expensive. Avoid using it where possible." – henriqueor Mar 11 '21 at 03:43
  • SingleChildScrollView perfectly solved my problem that keyboard cover my TextField which lead to can not see what is inputing. – JChen___ Oct 25 '21 at 02:40
  • thanks a lot!!!, this should be the correct answer! – Rohan Arora Jul 25 '23 at 05:33
39

Set resizeToAvoidBottomInset to false instead of resizeToAvoidBottomPadding which is deprecated.

    return Scaffold(
      resizeToAvoidBottomInset : false,
      body: YourWidgets(),
    );
Toothgip
  • 469
  • 6
  • 14
ArtiomLK
  • 2,120
  • 20
  • 24
  • 4
    They are the same answers @Ojonugwa, maybe the editor updated the accepted answer after I posted mine so now the difference is that mine includes an example? – ArtiomLK Jul 04 '20 at 03:19
36

My approach is to use SingleChildScrollView with the ClampingScrollPhysics physics.

SingleChildScrollView(
  physics: ClampingScrollPhysics(),
  child: Container(),
)
Den
  • 1,456
  • 16
  • 17
19

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()
        ],
      ),
    ),
  ),
);
Toni
  • 3,296
  • 2
  • 13
  • 34
10

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()
            ],
         ),
      ),
   ),
);
Saman
  • 503
  • 4
  • 18
8

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.

Phani Pavan
  • 354
  • 3
  • 8
7

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"

yatin deokar
  • 730
  • 11
  • 20
6

Setting the value false for resizeToAvoidBottomInset worked fine for me.

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

Shawn
  • 1,232
  • 1
  • 14
  • 44
Amimul Ihsan
  • 136
  • 1
  • 8
6

Feature:

  • Background image does not resize when keyboard is opened
  • Ability to scroll elements hidden behind the keyboard
import 'package:flutter/material.dart';

SizedBox addPaddingWhenKeyboardAppears() {
  final viewInsets = EdgeInsets.fromWindowPadding(
    WidgetsBinding.instance!.window.viewInsets,
    WidgetsBinding.instance!.window.devicePixelRatio,
  );

  final bottomOffset = viewInsets.bottom;
  const hiddenKeyboard = 0.0; // Always 0 if keyboard is not opened
  final isNeedPadding = bottomOffset != hiddenKeyboard;

  return SizedBox(height: isNeedPadding ? bottomOffset : hiddenKeyboard);
}

/// The size of the screen.
class ScreenSizeService {
  final BuildContext context;

  const ScreenSizeService(
    this.context,
  );

  Size get size => MediaQuery.of(context).size;
  double get height => size.height;
  double get width => size.width;
}

class LoginPage extends StatelessWidget {
  final _imageUrl =
      'https://images.unsplash.com/photo-1631823460501-e0c045fa716f?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60';

  const LoginPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final screenWidth = ScreenSizeService(context).width;
    final screenHeight = ScreenSizeService(context).height;

    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(_imageUrl),
            fit: BoxFit.cover,
          ),
        ),
        child: SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(
              minWidth: screenWidth,
              minHeight: screenHeight,
            ),
            child: Column(
              children: [
                ...List.generate(6, (index) {
                  return Column(
                    children: [
                      Container(
                        height: 60,
                        width: double.maxFinite,
                        color: Colors.pink[100],
                        child: Center(child: Text('$index')),
                      ),
                      const SizedBox(height: 40),
                    ],
                  );
                }),
                Container(color: Colors.white, child: const TextField()),
                addPaddingWhenKeyboardAppears(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
Yauheni Prakapenka
  • 1,056
  • 11
  • 10
3

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

aptik
  • 583
  • 1
  • 4
  • 8
  • 5
    But this still doesn't make the TextField you selected to be visible, the keyboard will still overlap it. You have to scroll it yourself. Not used to as an Android developer / user – Boy Apr 15 '18 at 12:23
3

i had same problem with my screen and here how i fix it :

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

enter image description here

Zakaria Ferzazi
  • 364
  • 3
  • 4
2

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,
      ),
    ],
  ),
 ),
);
Deepak Jha
  • 1,539
  • 12
  • 17
  • 7
    you are aware of the fact that this will immediately throw a mass of errors because the expanded widget will have infinite space to fill given by the singlechildscrollview – Christian X Nov 27 '20 at 09:14
2

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

Javeed Ishaq
  • 6,024
  • 6
  • 41
  • 60
2

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

Pumuckelo
  • 323
  • 1
  • 9
  • 1
    From the [docs of IntrinsicHeight](https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html) (emphasis mine): "This class is relatively expensive, because it adds a speculative layout pass before the final layout phase. **Avoid using it where possible.** In the worst case, this widget can result in a layout that is O(N²) in the depth of the tree.". Advertising it as perfect using a widget that is recommended to be avoided if possible with plenty alternatives around might be a bit misleading. – geisterfurz007 Aug 11 '21 at 07:14
2

This will scroll with your keypad and collapse size when keyboard disappears.

showModalBottomSheet(
      isScrollControlled: true,
      context: context,
      builder: (context) {
        return Padding(
            padding: MediaQuery.of(context).viewInsets,
            child:SingleChildScrollView(
            physics: ClampingScrollPhysics(),
            child: Container(.......)));
Emir Bolat
  • 899
  • 3
  • 14
  • 37
2

You can use

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

or you can wrap your widgets in

SingleChildScrollView
vishnu reji
  • 193
  • 2
  • 11
1

According to the latest version of flutter resizeToAvoidBottomPadding is deprecated and should not be used, now you must use resizeToAvoidBottomInset.

Example:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       resizeToAvoidBottomInset: false, // this property here
       body: ...
    )
  }
}

Warning: you can only use this if your Widget has a Scaffold, otherwise you'll have to search specifically your case.

1

For a login screen or similar, in addition to solutions provided by all other top answers, I would also add reverse: true on SingleChildScrollView. These screens typically consist of some image or logo at the top half, and some text fields (username, password) and a button (sign in) at the bottom.

So in this particular case (which I constantly encounter, and I refer to this thread mostly because of it), it makes sense to scroll all the way down, so that all the text fields and button(s) are visible.

Code:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      body: SingleChildScrollView(
        reverse: true,
        physics: const ClampingScrollPhysics(),
        child: SizedBox(
          height: MediaQuery.of(context).size.height,
          child: PutYourContentHere(),
        ),
      ),
    );
  }
Ashkan Sarlak
  • 7,124
  • 6
  • 39
  • 51
0

This strange behavoiur of media query when we click on textfield and keyBoard open media query rebulid one more same page in stack.

MaterialApp( useInheritedMediaQuery: true,)

useInheritedMediaQuery to true will help you.

Muhammad Nadeem
  • 193
  • 1
  • 7
0

Within the Scaffold, set resizeToAvoidBottomInset property to false.

However, if you want to have certain Widgets resize within a ListView after setting the Scaffold, then here is a solution I am currently using:

class _YourCustomWidgetState extends State<YourCustomWidget> {

  final ScrollController _scrollController = ScrollController();
  final FocusNode _focusableWidgetNode = FocusNode();

  @override
  Widget build(BuildContext context) {

    final double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;

    // This is just if you want to have the last focusable Widget scroll into view:
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      if (_focusableWidgetNode.hasPrimaryFocus) {
        // Tweak this if you want to change where you want to scroll to but 
        // it should be rarely necessary:
        final double bottomOffset = _scrollController.position.maxScrollExtent;
        _scrollController.animateTo(bottomOffset,
            duration: Duration(milliseconds: 100), curve: Curves.linear);
      }
    });

  return Column(
   children: [
    // Widgets
    ListView(
      controller: _scrollController,
      shrinkWrap: true,
      padding: EdgeInsets.only(
         // Just ensure this is set:
         bottom: keyboardHeight
      ),
      children: [
        // Widgets
      ],
    ),
    // Widgets
  ],
);
  }

}

Alternatively, a SingleChildScrollView as suggested by others is another way but I've found it doesn't work well if you have Widgets between a ListView like I do.

Adam Bridges
  • 306
  • 3
  • 7
-2

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.

Rudr Thakur
  • 330
  • 3
  • 12