When the keyboard appears, the Flutter widgets resize. How to prevent this?
Asked Answered
P

24

288

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.

Planish answered 3/10, 2017 at 18:37 Comment(3)
In your Scaffold body use a SingleChildScrollView.Felicific
This example will help you - Flutter login page With Background ImageUnclad
For future readers : If possible then remove the scaffold it will solve the problemXiomaraxiong
O
649

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.

Optime answered 3/10, 2017 at 18:45 Comment(7)
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.Mccauley
I do not know about android, but it is generally a good practice to wrap your layout inside a ListView in this caseOptime
@KishanVyas same with me image is gone.Baggage
resizeToAvoidBottomPadding is deprecated, use resizeToAvoidBottomInset check this AnswerCarinthia
This does nothing of the sort suggested in the question. All widgets underneath the keyboard still resize.Urtication
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://mcmap.net/q/101125/-how-can-i-avoid-the-background-image-to-shrink-when-my-keyboard-is-activeImogen
it works when I adding "resizeToAvoidBottomInset: false,"Notions
M
128

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.

Macri answered 16/2, 2020 at 6:53 Comment(6)
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 resizeToAvoidBottomInsetLiberate
Set the primary property to false.Pursuit
but using NeverScrollableScrollPhysics makes it so it doesn't get scrolled up with the keyboard too.Confirmation
remove, NeverScrollableScrollPhysics() line of the SingleChildScrollView Widget, or replace ClampingScrollPhysics() to there. then it will scroll up with the keyboard.Flapjack
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."Drava
SingleChildScrollView perfectly solved my problem that keyboard cover my TextField which lead to can not see what is inputing.Cousingerman
C
42

Set resizeToAvoidBottomInset to false instead of resizeToAvoidBottomPadding which is deprecated.

    return Scaffold(
      resizeToAvoidBottomInset : false,
      body: YourWidgets(),
    );
Carinthia answered 12/5, 2019 at 0:29 Comment(1)
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?Carinthia
B
42

My approach is to use SingleChildScrollView with the ClampingScrollPhysics physics.

SingleChildScrollView(
  physics: ClampingScrollPhysics(),
  child: Container(),
)
Brassiere answered 2/8, 2020 at 14:52 Comment(0)
G
22

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()
        ],
      ),
    ),
  ),
);
Gristmill answered 31/10, 2020 at 17:45 Comment(0)
K
11

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()
            ],
         ),
      ),
   ),
);
Kery answered 14/1, 2021 at 19:33 Comment(0)
C
10

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.

Caesura answered 17/2, 2021 at 3:13 Comment(0)
C
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"

Canna answered 13/11, 2019 at 12:9 Comment(0)
U
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(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
Unity answered 18/9, 2021 at 12:6 Comment(0)
A
6

Setting the value false for resizeToAvoidBottomInset worked fine for me.

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

Approbation answered 18/1, 2021 at 18:2 Comment(4)
You cannot use either one, the latter is deprecated with Flutter v1.1.9. docs.flutter.dev/release/breaking-changes/…Conclusion
Would be more useful if you mentioned where that goesBiflagellate
@Biflagellate You can set the value to inside Scaffold WidgetApprobation
@Conclusion You are right. Now, you need to set the value for resizeToAvoidBottomInset.Approbation
C
4

My approach is to use SingleChildScrollView with the BouncingScrollPhysics physics.

 SingleChildScrollView(
  physics: BouncingScrollPhysics(),
  child: Container(),
) 
Curmudgeon answered 15/9, 2023 at 7:19 Comment(0)
D
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

Disbelief answered 4/10, 2017 at 3:10 Comment(1)
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 / userGooden
D
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,
      ),
    ],
  ),
 ),
);
Divulge answered 9/3, 2020 at 14:20 Comment(1)
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 singlechildscrollviewHaplography
K
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

Kassiekassity answered 22/8, 2020 at 4:30 Comment(1)
this will be only working for android as it appears...Haplography
G
3

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

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

enter image description here

Gesticulation answered 8/11, 2022 at 14:53 Comment(0)
P
3

You can use

Scaffold(
  resizeToAvoidBottomInset: false, 
  ... 
)

or you can wrap your widgets in

SingleChildScrollView
Planoconcave answered 30/12, 2022 at 13:16 Comment(0)
P
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

Panzer answered 25/1, 2021 at 20:18 Comment(1)
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.Aime
I
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(.......)));
Irreclaimable answered 25/3, 2022 at 11:12 Comment(0)
S
2

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(),
        ),
      ),
    );
  }
Settle answered 8/8, 2023 at 23:11 Comment(0)
G
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.

Gulf answered 2/8, 2023 at 20:52 Comment(0)
A
1

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

Antre answered 16/12, 2023 at 17:1 Comment(0)
S
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.

Simple answered 4/9, 2022 at 5:56 Comment(0)
S
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.

Suk answered 22/6, 2023 at 23:24 Comment(0)
I
-2

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.

Intake answered 12/12, 2020 at 10:34 Comment(4)
Posting screenshots of code is discourated as it doesn't allow members to copy and play around with it themselves in easy fashion.Aime
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.Intake
@RudrThakur copy it from your screenshot :PDeuterogamy
deleted the image because some people don't understand that everyone is a beginner at some pointIntake

© 2022 - 2024 — McMap. All rights reserved.