Reputation: 20495
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.
Upvotes: 297
Views: 314732
Reputation: 4820
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.
Upvotes: 1
Reputation: 403
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,
),
),
Upvotes: 1
Reputation: 1286
I've built this reusable widget which expands to fit the screen if collapsed and collapses if the keyboard is shown.
child: IntrinsicHeightScrollView(
child: Column(
children: [
...
Spacer(), // Add spacer where you need the view to expand
...
],
)
),
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,
),
),
);
},
);
}
}
Upvotes: 3
Reputation: 66
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
Upvotes: 3
Reputation: 101
My approach is to use SingleChildScrollView
with the BouncingScrollPhysics physics.
SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Container(),
)
Upvotes: 4
Reputation: 410
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.
Upvotes: 0
Reputation: 7344
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(),
),
),
);
}
Upvotes: 3
Reputation: 131
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.
Upvotes: 1
Reputation: 518
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.
Upvotes: 1
Reputation: 2260
Set resizeToAvoidBottomInset
to false
instead of resizeToAvoidBottomPadding
which is deprecated.
return Scaffold(
resizeToAvoidBottomInset : false,
body: YourWidgets(),
);
Upvotes: 44
Reputation: 398
You can use
Scaffold(
resizeToAvoidBottomInset: false,
...
)
or you can wrap your widgets in
SingleChildScrollView
Upvotes: 5
Reputation: 69389
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.
Upvotes: 132
Reputation: 472
i had same problem with my screen and here how i fix it :
Scaffold(
resizeToAvoidBottomInset: false,
...
)
Upvotes: 4
Reputation: 320
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.
Upvotes: 0
Reputation: 21
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(.......)));
Upvotes: 2
Reputation: 1734
Feature:
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(),
],
),
),
),
),
);
}
}
Upvotes: 7
Reputation: 156
Setting the value false
for resizeToAvoidBottomInset
worked fine for me.
Also, resizeToAvoidBottomPadding
worked fine for me. You can use either one.
Upvotes: 6
Reputation: 385
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.
Upvotes: 12
Reputation: 389
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
Upvotes: 2
Reputation: 523
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()
],
),
),
),
);
Upvotes: 12
Reputation: 53347
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
.
Upvotes: 667
Reputation: 5155
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()
],
),
),
),
);
Upvotes: 23
Reputation: 7105
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
Upvotes: 3
Reputation: 1725
My approach is to use SingleChildScrollView
with the ClampingScrollPhysics
physics.
SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Container(),
)
Upvotes: 43
Reputation: 1609
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,
),
],
),
),
);
Upvotes: 3
Reputation: 781
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"
Upvotes: 7