When the keyboard appears, the Flutter widgets resize. How to prevent this?
KeyboardResizeWidgetTextfieldFlutterKeyboard Problem Overview
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:
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:
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.
Keyboard Solutions
Solution 1 - Keyboard
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
.
Solution 2 - Keyboard
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.
Solution 3 - Keyboard
Set resizeToAvoidBottomInset
to false
instead of resizeToAvoidBottomPadding
which is deprecated.
return Scaffold(
resizeToAvoidBottomInset : false,
body: YourWidgets(),
);
Solution 4 - Keyboard
My approach is to use SingleChildScrollView
with the ClampingScrollPhysics
physics.
SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Container(),
)
Solution 5 - Keyboard
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()
],
),
),
),
);
Solution 6 - Keyboard
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()
],
),
),
),
);
Solution 7 - Keyboard
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"
Solution 8 - Keyboard
Setting the value false
for resizeToAvoidBottomInset
worked fine for me.
Also, resizeToAvoidBottomPadding
worked fine for me. You can use either one.
Solution 9 - Keyboard
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
Solution 10 - Keyboard
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(),
],
),
),
),
),
);
}
}
Solution 11 - Keyboard
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,
),
],
),
),
);
Solution 12 - Keyboard
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
Solution 13 - Keyboard
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
Solution 14 - Keyboard
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.
Solution 15 - Keyboard
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.
Solution 16 - Keyboard
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(.......)));