301

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.

3

28 Answers 28

676

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.

Sign up to request clarification or add additional context in comments.

7 Comments

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.
I do not know about android, but it is generally a good practice to wrap your layout inside a ListView in this case
@KishanVyas same with me image is gone.
resizeToAvoidBottomPadding is deprecated, use resizeToAvoidBottomInset check this Answer
This does nothing of the sort suggested in the question. All widgets underneath the keyboard still resize.
|
132

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.

6 Comments

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
Set the primary property to false.
but using NeverScrollableScrollPhysics makes it so it doesn't get scrolled up with the keyboard too.
remove, NeverScrollableScrollPhysics() line of the SingleChildScrollView Widget, or replace ClampingScrollPhysics() to there. then it will scroll up with the keyboard.
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."
|
44

Set resizeToAvoidBottomInset to false instead of resizeToAvoidBottomPadding which is deprecated.

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

1 Comment

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?
43

My approach is to use SingleChildScrollView with the ClampingScrollPhysics physics.

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

Comments

24

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()
        ],
      ),
    ),
  ),
);

Comments

13

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.

Comments

12

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()
            ],
         ),
      ),
   ),
);

Comments

8

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"

Comments

7

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(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Comments

6

Setting the value false for resizeToAvoidBottomInset worked fine for me.

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

4 Comments

You cannot use either one, the latter is deprecated with Flutter v1.1.9. docs.flutter.dev/release/breaking-changes/…
Would be more useful if you mentioned where that goes
@West You can set the value to inside Scaffold Widget
@DarkMikey You are right. Now, you need to set the value for resizeToAvoidBottomInset.
5

You can use

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

or you can wrap your widgets in

SingleChildScrollView

Comments

4

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

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

enter image description here

Comments

4

My approach is to use SingleChildScrollView with the BouncingScrollPhysics physics.

 SingleChildScrollView(
  physics: BouncingScrollPhysics(),
  child: Container(),
) 

Comments

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

1 Comment

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
3

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,
      ),
    ],
  ),
 ),
);

1 Comment

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
3

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 Comment

this will be only working for android as it appears...
3

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(),
        ),
      ),
    );
  }

Comments

3

The Scaffold property resizeToAvoidBottomInset does the job. And from the documentation, https://api.flutter.dev/flutter/material/Scaffold/resizeToAvoidBottomInset.html, this property is a boolean, so setting it to false does the trick. Here is a use case:

@override
Widget build(BuildContext context) {
  return Scaffold(
    resizeToAvoidBottomInset: false,
    backgroundColor: Colors.grey[300],
    appBar: AppBar(
      toolbarHeight: 50,
      leading: const Icon(
        Icons.menu,
        color: Color.fromARGB(255, 138, 60, 55),
      ),
      title: const Text(
        'Tokyo',
        style: TextStyle(
          fontFamily: 'DM Serif Display',
          fontSize: 30.0,
          color: Color.fromARGB(255, 138, 60, 55),
        ),
      ),
      centerTitle: true,
      backgroundColor: Colors.grey[300],
    ),
    body: Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          // Add your widgets here
        ],
      ),
    ),
  );
}

A screenshot is also provided.

resizeToAvoidBottomInset example

Comments

3

I've built this reusable widget which expands to fit the screen if collapsed and collapses if the keyboard is shown.

How to use

child: IntrinsicHeightScrollView(
  child: Column(
    children: [
      ...
      Spacer(), // Add spacer where you need the view to expand  
      ...
    ],
  )
),

Screenshots

Expanded Collapsed
enter image description here enter image description here

Source

import 'package:flutter/material.dart';

/// A scrollable view that enforces an intrinsic height based on its content.
///
/// The `IntrinsicHeightScrollView` widget is used to create a scrollable
/// view that adjusts its height to fit its content. It ensures that the
/// child widget inside it does not overflow vertically.
///
/// Example usage:
///
/// ```dart
/// IntrinsicHeightScrollView(
///   child: LoginForm(),
/// )
/// ```
///
/// In this example, the `IntrinsicHeightScrollView` will adjust its height
/// to accommodate the `LoginForm` widget without causing vertical overflow.
final class IntrinsicHeightScrollView extends StatelessWidget {
  /// Creates an `IntrinsicHeightScrollView` with the specified [child].
  ///
  /// The [child] is the widget that will be displayed within the scroll view.
  const IntrinsicHeightScrollView({
    required this.child,
    Key? key,
  }) : super(key: key);

  /// The child widget to be displayed within the scroll view.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraint) {
        return SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(minHeight: constraint.maxHeight),
            child: IntrinsicHeight(
              child: child,
            ),
          ),
        );
      },
    );
  }
}

Comments

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

1 Comment

From the docs of IntrinsicHeight (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.
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(.......)));

Comments

1

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.

Comments

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.

Comments

1

Using SingleChildScrollView with below provided physics and primary along with ConstrainedBox and IntrinsicHeight worked for me.

return Scaffold(
  body: SingleChildScrollView(
    physics:
        const ClampingScrollPhysics(parent: NeverScrollableScrollPhysics()),
    primary: false,
    child: ConstrainedBox(
      constraints: BoxConstraints(
        minWidth: MediaQuery.of(context).size.width,
        minHeight: MediaQuery.of(context).size.height,
      ),
      child: IntrinsicHeight(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              "SIGNUP",
              style: TextStyle(
                fontSize: largeTextSize,
                fontWeight: FontWeight.bold,
              ),
            ),

Comments

1

In my case I was using the form inside a dialog.

To prevent the widget overflow I used the scrollable attribute from the AlertDialog, setting it to true solved my problem.

Comments

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.

Comments

0

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

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.

4 Comments

Posting screenshots of code is discourated as it doesn't allow members to copy and play around with it themselves in easy fashion.
Hey, I was new to stackoverflow that time and I did not know how to add code to your answer. I would have edited the answer later, but I do not have the code anymore.
@RudrThakur copy it from your screenshot :P
deleted the image because some people don't understand that everyone is a beginner at some point
0

Flutter: Background Image Moves Up When Tapping on TextFormField – How to Prevent Layout Shift?

I have a CustomScaffold widget that contains a background image, and the body of the screen is placed inside a Scaffold widget. However, when I tap on a TextFormField, the background image shifts upward along with the body content. I want to prevent the background image from shifting when the keyboard appears without breaking my layout.

What I Tried:

  • I set resizeToAvoidBottomInset: false in my Scaffold, which should have prevented the body from resizing when the keyboard appears, but the background image still shifts up.

  • I wrapped the scrollable elements with a SingleChildScrollView to make it scrollable, but it didn’t fix the issue.

What Worked:

I found a solution by wrapping the background image in a SingleChildScrollView. This allowed the background to remain fixed while the body could still scroll independently. Below is my updated CustomScaffold widget that fixed the issue:

class CustomScaffold extends StatelessWidget {
  final Widget body;
  final Color? backgroundColor;
  final PreferredSizeWidget? appBar;
  final Widget? bottomNavigationBar;
  final bool? extendBodyBehindAppBar;
  final bool? resizeToAvoidBottomInset;
  final bool bgImg;
  final String productType;

  const CustomScaffold({
    Key? key,
    required this.body,
    this.backgroundColor,
    this.appBar,
    this.bottomNavigationBar,
    this.extendBodyBehindAppBar = false,
    this.resizeToAvoidBottomInset,
    this.bgImg = false,
    this.productType = "",
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
      },
      child: Stack(
        children: [
          bgImg == false && productType.isEmpty
              ? SizedBox.shrink()
              : Positioned.fill(
                  left: 0,
                  top: 0,
                  right: 0,
                  bottom: 0,
                  child: SingleChildScrollView( // Added SingleChildScrollView
                    child: Image.asset(
                      _getBackgroundImage(productType),
                      fit: BoxFit.fill,
                    ),
                  )),
          Scaffold(
            extendBodyBehindAppBar: extendBodyBehindAppBar ?? false,
            resizeToAvoidBottomInset: resizeToAvoidBottomInset,
            appBar: appBar,
            backgroundColor: backgroundColor ??
                (bgImg == false
                    ? Constants.colors.background
                    : Colors.transparent),
            body: body,
            bottomNavigationBar: bottomNavigationBar,
          ),
        ],
      ),
    );
  }

  String _getBackgroundImage(String? productType) {
    switch (productType) {
      case "A":
        return 'assets/images/pl_bg.webp';
      case "B":
        return 'assets/images/pl_bg.webp';
      case "C":
        return 'assets/images/pl_bg.webp';
      case "D":
        return 'assets/images/pl_bg.webp';
      case "E":
        return 'assets/images/pl_bg.webp';
      case "F":
        return 'assets/images/pl_bg.webp';
      case "G":
        return 'assets/images/ce_bg.webp';
      default:
        return 'assets/images/splash.webp';
    }
  }
}

and here is my UI

child: CustomScaffold(
            resizeToAvoidBottomInset: false,
bgImg : true,
productType: "A",
            body: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                LabelWidget(
                  text: 'My UI'.tr,
                  fontWeight: FontWeight.w600,
                  fontSize: 28.sp,
                  textColor: Colors.black,
                  padding: 0,
                ).marginSymmetric(horizontal: 16.w),

                // Select the card for offers
                CardWidgetForOffers(
                    offerController: controller,
                    entitiesController: entController), // select card
                16.verticalSpace,

                Expanded(
                  child: SingleChildScrollView(
                    controller: scrollController,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        // Banners widget  swipe cards
                        BannerWidget(),

                        // saved offers
                        SavedOffersWidget(
                            offersController: controller),

                        // search offers here [This is my TextFormField]
                        SearchOffersWidget(offersController: offersController),

                        //  offers in list view
                        NewTestListViewWidget(),
                        15.verticalSpace,
                      ],
                    ),
                  ),
                ),
              ],
            ).marginOnly(top: 50.h),
          ),

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.