Please refer to the Widget Text("HERE") and corresponding ScreenShot below.
When a keyboard appears from the bottom of device, the Widget Text("HERE") relatively moved to upper-side, hence I should care about overflow of whole widget size as well as size of user devices.
How can I locate this Widget absolutely, or should I always make all things (widget) scrollable to corresponds to any devices and also to avoid overflow problem ?
Stack(
children:[
,//omit
const Align(
alignment: Alignment.bottomCenter,
child: Text("HERE"),
)
]
)
first, make sure you constrained your Scaffold widget to the full-screen height, with MediaQuery:
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height,
),
child: Scaffold(// your screen code),
),
then wrap your Text with a Positioned widget, then set the bottom property to 0 or the value you want to bottom with:
// ...
child: Stack(
children:[
Positioned(
bottom: 0,
child: Text("HERE"),
)
]
)
now even the keyboard is on, the screen will not resize and the Text widget will stay forcelly in the bottom of screen.
just add this line in Scaffold
Scaffold(
...
resizeToAvoidBottomInset : ture
);
hope it helps :)
Related
Say there is a Scaffold with a bottomNavigationBar property that is populated. If you use a Stack with a Align widget inside its body set to Alignment.center, the Align widget gets centered accounting to the bottomNavigationBar and centers in the remaining space minus that.
How do I get it so that it centers according to the total screen height, using all these widgets?
Stack(
body: Align widget with center alignment and some child,
bottomNavigationBar: Bottom app bar
)
I'm not entirely sure what you mean in your question - but I think you want the body of a Scaffold (not Stack) to center a widget for the whole height available on the device.
Here's how you could do that using a Scaffold, SizedBox and Center widgets along with a MediaQuery
Scaffold(
body: SizedBox(
height: MediaQuery.of(context).size.height,
child: Center(
child: Text("CENTERED")),
),
bottomNavigationBar: BottomNavigationBar(items: []),
)
Scaffold
Sized Box
Center
Media Query
Hopefully that puts you on the right path.
I have the following in my widget's build method:
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Title bar'),
),
body: Center(
child: SizedBox(
width: 250,
child: ListView(
children: [
const Text('Text goes here'),
Container(
width: 250,
height: 250,
color: Colors.green,
child: Texture(textureId: textureId)), // ID of a texture that displays pixels
],
),
),
),
floatingActionButton: FloatingActionButton(
child: const Text('Button'),
onPressed: () {
noisy();
},
),
));
What I want to have take place is that the Texture widget is always 250x250 pixels, regardless of what I resize the window to. When I resize the window vertically, this is what happens, and the overflow of the widget that goes past the bottom of the screen is just clipped and ignored. However, when I resize horizontally making the window less wide than the widget, the widget is scaled horizontally, getting compressed in that direction instead of being clipped to the right. What arrangement if widgets would I need in order to keep the Container/Texture at 250x250 pixels and have it clipped past the end of the window instead of being compressed?
I have tried surrounding the inner Container in another horizontal ListView, as the outer ListView appears to clip the window vertically correctly, but that causes RenderBox was not laid out: RenderRepaintBoundary#51231 relayoutBoundary=up5 NEEDS-PAINT.... I also attempted placing that inner ListView in another Container/SizedBox, though this did not fix the shrinking issue and cause it to clip instead.
Try using unconstrainedBox
UnconstrainedBox(
child: SizedBox(
)
)
This allows a child to render at the size it would render if it were alone on an infinite canvas with no constraints. This container will then attempt to adopt the same size, within the limits of its own constraints. If it ends up with a different size, it will align the child based on alignment. If the box cannot expand enough to accommodate the entire child, the child will be clipped.
In debug mode, if the child overflows the container, a warning will be printed on the console, and black and yellow striped areas will appear where the overflow occurs.
simple, use ConstraintBox & set it minHeight and minWidth
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 100,
minHeight: 100,
maxWidth: 100,
maxHeight: 100,
),
)
i have broadcast screen ( in full screen) . i have textField on the same broadcast screen on the bottom so we have here Stack like following
Scaffold(
body: Stack(
alignment: Alignment.bottomCenter,
children:[
broadCastScreen()
TextField()
]
)
)
so my layout looks like this
now when i tap on the text field all scaffold resize its self because of the keyboard show up
the good point in this part that my textfieldalways smoothly move to be up the keyboard visible
but the bad part that my broadcast screen resize its self inappropriately for the user experience
so i decide to prevent that annoying party with set resizeToAvoidBottomInset to false
well now everything sound good , but i noticed that my keyboard covering my text field
so How Can i use resizeToAvoidBottomInsetto all my widgets except my textfield part ?
i tried to wrap my textfield with another scaffold with set resizeToAvoidBottomInset to true
and scaffold color to transparent so others widgets be visible and tis work as expected but i completely faced problems with GestureDetector pointers in my first scaffold and thats because my second scaffold stack it
another solution i tried programmatically to set my textfield bottom padding to be the same of my keyboard height using EdgeInsets.fromWindowPadding(WidgetsBinding.instance.window.viewInsets,WidgetsBinding.instance.window.devicePixelRatio);
but that's not perfect way because there is some delay with height keyboard value like 900 millisecond to get the keyboard height Furthermore it i tested it on several difference phones and i got null height with some devices It does not guarantee the measurement of all phones
any other perfect way friend ?
Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
fit: StackFit.expand,
children: [
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Content()
]
Positioned(
bottom: 0,
child:
child: TextFormField(
decoration: InputDecoration(filled: true, fillColor:
Colors.red
),
),
)
],
),
);
Simply Wrap your content in SingleChildScrollView and wrap your TextField in Positioned widget set bottom parameter of Positioned to Zero, add
fit: StackFit.expand,
Note: Don't forget to add above code to your Stack Widget
Let's try to do something like this
Scaffold(
body: Stack(
alignment: Alignment.bottomCenter,
children:[
Positioned.fill(child:
broadCastScreen()),
Position(
bottom:0,
child: TextField())
]
)
)
In the screen below, I have the following text "By creating an account ...." that I would like to always keep at the bottom of the screen and should never scroll up with the keyboard.
Currently the text is placed in bottom sheet but unfortunately, the bottom sheet does scroll up with the keyboard.
Using resizeToAvoidBottomInset: false in the Scaffold will not help since it will make all screen ignore the keyboard.
Is there any way to keep the text at the bottom of the screen and have it ignore the keyboard?
I was able to find a basic solution to this.
I placed the terms and conditions text in the bottomNavigationBar property of the Scaffold like this.
Scaffold(
bottomNavigationBar: const TermsAndConditionsText(),
....
....
)
The trick is to use the MediaQuery.of(context).viewInsets.bottom
The value of .bottom will be updated whenever the Keyboard shows up.
To achieve what you want
1- Define the resizeToAvoidBottomInset: false in the Scaffold.
2- Wrap your "form" in a Stack widget.
3- Add your Widgets which should stick at the bottom of the screen at the same level of your "form" in an Align or Positioned
Stack(
children: [
child: Column(
... //Form
),
const Align(
alignment: Alignment.bottomCenter,
child: Text("Terms And Conditions"),
)
],
),
4- And now the magic trick : wrap your "form" in a Padding with the viewInsets.bottom value
Stack(
children: [
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
...// Form
),
),
const Align(
alignment: Alignment.bottomCenter,
child: Text("Terms and Conditions"),
)
],
),
My solution looks more like a "hack" than a proper solution, but I never found any official documentation solving this kind of "problem".
As far as I see, Column and ListView both have a very distinct usage when used for a base root layouting.
Column is used when the screen has few components (such as login screen). We can add some Expanded components to adjust white spaces in between, so when the keyboard is visible, the screen shrink to keep everything visible.
ListView is used when the screen has many components that potentially need scrolling. We can't use Expanded component in ListView. When using ListView, appearing keyboard does not change the white spaces, only change the size of outer ListView, while the inner content is wrapped in scroll view.
Now the problem is, how if I want to have screen like this:
When all the contents' combined vertical size is not longer than available height quota given from parent (in this case, screen's height), then the components behave like inside Column: expanding or shrinking to fill available white spaces according to rules set by Expanded.
When all the content's combined vertical size is longer than available height quota, then the components behave like inside ListView: all the possible expanding components will shrink into their minimum size (ignoring Expanded), and the screen is scrollable so user can see the rest of the screen below.
Is this possible to be done in Flutter? How?
EDIT: based on Reign's comment, I have isolated some code from SingleChildScrollView manual, but it looks like it still can't handle if its children contains Expanded.
Widget columnRoot({
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceBetween,
AssetImage backgroundImage,
List<Widget> children
}) =>
LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) =>
SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: backgroundImage,
fit: BoxFit.cover),
color: Colors.white
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: mainAxisAlignment,
children: children
),
)
)
)
);
Widget content(BuildContext context) => columnRoot(children: [
Container(color: Colors.red, height: 100.0),
Expanded(Container(color: Colors.green)), // without this line, there's no layout error
Container(color: Colors.blue, height: 100.0),
]);
Error:
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
I added some code you can test with also with some explanation.
Copy paste and run the code
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SingleChildScrollView( //Since setting it to scrollable, your widget Column with expanded children wont work as it supposed to be because it wont know its parent height
//Since its already scrollable `Expanded` will expand or shrink now based on it child widget (Expanded(child: SomeHeight widget)) refer: #10 example
child: IntrinsicHeight( //This will fix the expanded widget error
child: Container(
//Test remove this height
// height: 400, //But when you set its height before its parent scroll widget, `Expanded` will expand based on its available space
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(color: Colors.red, height: 100.0),
//#10
//Experiment with this
Expanded(
child: Container(
color: Colors.purple,
// height: 100.0, //initialized height, remove parent container height: 400
// child: Text("This is also considered as min height"),
),
),
Container(color: Colors.blue, height: 100.0),
],
),
),
),
),
),
);
}
}