Keep Flutter widget at fixed size upon resizing window - flutter

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

Related

How to locate the bottom widget absolutely?

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 :)

In Flutter, how can I have an adjusting vertical layout, a mix between column and listview behavior?

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

Flutter: layout question regarding column child height

I have a layout where I want to expand a widget (container) to the right, it's the red area in the examples. To the left of the expanded container there is a column with an image at the topleft (blue in examples) and a button in the bottom of the column. I do know the button width, it's a bit over 100 pixels but we can assume that it's 100 pixels if that helps.
The thing is that the blue area (it's a user uploaded image) can vary in size. From e.g. 100x100 to 800x800. It will be square and smaller than 100x100 is not supported. Large images are resized to 800x800.
I want to achieve this:
The column should adapt it's size depending on the image size.
The column should be as wide as needed by the button but otherwise 100 <= width <= 400 depending on image size.
The column should not overflow in any direction.
The red area should be maximized.
The button should always be at the bottom left of the layout.
I cannot know the column height in advance without using a LayoutBuilder and I want to know if this is achievable without calculating the exact height in pixels using a LayoutBuilder.
Here's an example of how I want it too look with a small image (100x100) and that works with the code below the image:
Code that works well for 100x100 image:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Container(
height: 400,
width: 800,
child: Row(
children: <Widget>[
_buildColumn(),
Expanded(child: Container(color: Colors.red)),
],
),
),
),
);
}
Widget _buildColumn() {
return Container(
constraints: BoxConstraints(maxWidth: 400),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildImage(),
MaterialButton(
color: Colors.green,
child: Text('Fixed height button'),
onPressed: () {},
)
],
),
),
);
}
Widget _buildImage() {
// This can vary from 100x100 to 800x800 (always square)
var size = 100.0;
return Container(color: Colors.blue, width: size, height: size);
}
If I bump it to an 800x800 image by changing the size variable to 800.0 in _buildImage() I get this layout:
I understand that the column have unconstrained height on it's children so it will render the blue container with 800px height which creates the overflow. I do know that the column height is 400px in this case and that the button is 48px high so I can calculate that the max image size is 352px in height. By wrapping the blue container in a Container with constraints (maxWidth: 352, maxHeight: 352) I achieve the layout that I want:
Widget _buildImage() {
// This can vary from 100x100 to 800x800 (always square)
var size = 800.0;
return Container(
constraints: BoxConstraints(maxWidth: 352, maxHeight: 352),
child: Container(color: Colors.blue, width: size, height: size),
);
}
I can also achieve this using expanded, like this:
Widget _buildImage() {
// This can vary from 100x100 to 800x800 (always square)
var size = 800.0;
return Expanded(
child: Container(color: Colors.blue, width: size, height: size),
);
}
I want to avoid using calculated pixels for height/width so lets continue with the Expanded widget. When I have that expanded widget with a small image, i.e. 100x100 I get this result (which is not what I want):
I need to align my blue square within the expanded widget to prevent it to getting stretched but when I do that (align top left), like this:
Widget _buildImage() {
// This can vary from 100x100 to 800x800 (always square)
var size = 100.0;
return Expanded(
child: Container(
alignment: Alignment.topLeft,
child: Container(color: Colors.blue, width: size, height: size),
),
);
}
Then I get this result:
The container outside of the blue container expands and makes the column unnecessary wide. I want to have the same look as the first image when I have a small image.
How can I achieve this adaptive layout without using a LayoutBuilder and calculating exact image constraints?
Wrap the column with a container and set the height:
MediaQuery.of(context).size.height * 0.8;
it will contain 80% height in screen

BoxConstraints forces an infinite height

child:Column(
children: <Widget>[
Container(
height: double.infinity,
width: 100.0,
color: Colors.red,
child: Text('hello'),
),)
in this,when i make height:double.infinity,it gives error in run saying **BoxConstraints forces an infinite height.**but when i give height manually it work fine.
can anyone explain me why this happening.
How about this one.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
Expanded(
child: Container(
// height: double.infinity,
width: 100.0,
color: Colors.red,
child: Text('hello'),
),
),
],
),
),
);
}
}
This means that you can't offer inifite height to the container. It's obvious behaviour if you don't provide the contraints to height.
You have to specify limited height to the container so that flutter can render it, if you offer it infinite it how can flutter render that and up to which constraints it would do that !
Rather you can set double.infinity to width and flutter will successfully render that because by default flutter has constraints for width it will set width to width of screen.
Considering that you have to provide height as that of screen you can use MediaQuery for that
Widget yourMethod(or build)(BuildContext context){
final screenHeight = MediaQuery.of(context).size.height;
return Column(
children:<Widget>[
Container(
height:screenHeight,//but this will be height of whole screen. You need to substract screen default paddings and height of appbar if you have one
width:100.0,
....
)
]);
}
Hope this helps !
Happy coding..
BoxConstraints forces an infinite height
Why This Happens
You're asking to render an infinite height object without a height constraint... Flutter can't do that.
Column lays out children in two phases:
Phase 1: non-Flex items (anything not Expanded, Flexible or Spacer)
done in unconstrained space
Phase 2: Flex items (Expanded,Flexible, Spacer only)
done with remaining space
Phase 1
Column's phase 1 vertical layout is done in unbounded space. That means:
no vertical constraint → no height limit
any widget with infinite height will throw the above error
you can't render an infinite height object in an infinite height constraint... that's goes on forever
Phase 2
after Phase 1 widgets have taken as much space as they intrinsically need, phase 2 Flex items share the remaining/leftover space
the remaining space is calculated from incoming constraints minus Phase 1 widgets dimensions
double.infinity height will expand to use up the remaining space
Infinite Height is OK
Here's an example of using infinite height on a Container inside a Column, which is fine:
class ColumnInfiniteChildPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Flexible(
child: Container(
height: double.infinity, // ← perfectly fine
child: Text('Column > Container > Text')),
),
Text('Column > Text')
],
),
),
);
}
}
Remove the Flexible and the error will be thrown.

How to arrange a border layout in flutter?

I'm trying to layout two widgets. The top one is a PhotoView and the bottom is a slider. I need the slider to take up as much room as it needs, and the rest to be taken up by the PhotoView.
I tried using a Column class to wrap everything.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GestureDetector(
onTapDown: (TapDownDetails details) {print("Tapped");}),
child: Column(children: [
Container(
child: Center(
child: Container(
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Container(
child: hasPermission
? PhotoView(
imageProvider:
FileImage(filename)),
)
: Text("Waiting"),
)),
)),
Slider(
min: 0,
max: 10,
value: 5,
onChanged: (double value) {
print(value);
},
),
])),
);
}
The problem is that it overflows on the bottom, saying that "BOTTOM OVERFLOWED BY infinity PIXELS".
I suspect that the reason is that PhotoView is unbounded, and so is column, but I need the PhotoView to be bounded by the limit of the view height - slider height.
It's not the slider which is unbounded. Wrap the top part (Container which indirectly contains PhotoView) in Expanded (https://docs.flutter.io/flutter/widgets/Expanded-class.html).
I'd recommend not going the route of computing heights and widths manually, as often the same effect can be achieved without it and then you get reacting all dynamic window geometry changes (like phone orientation changing) "for free".