This is my Container:
Container(
height: height - height * 0.4, //this is my preferred height.
width: width - width * 0.7,
decoration: decoration,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset(
'assets/opensource.png',
scale: 1.35,
),
text('Open Source Software', 20, Color(0xFF02bbe5)),
text('Contributor since December, 2020', 16,
Color(0xFF02bbe5)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: text(
'test',
16,
Colors.blueGrey),
),
],
),
),
),
I need to make the app responsive, for that the Container needs to be elongated when the inner widgets don't fit inside the Container properly.
How do I give a preferred height to this Container but also allow it to resize freely in case of overflow?
Note: This Container is inside a Column itself with SingleChildScrollView as the parent. And I am building a web app.
Edit: I have used MediaQuery for getting width and height.
There are multiple ways to approach this problem. You can wrap you container inside an AspectRatio widget and an Expanded.
AspectRatio(
aspectRatio: someAspectRatioValue,
child: Expanded(
child Container(...)
)
)
This should expand the items container without loosing shape of the container.
You can also build your whole screen with a LayoutBuilder, though that would require a lot of reworking.
If you want to make your application responsive in Flutter then you should Sizer package( Here ). This package will make your app responsive for any screen size, it divides into percentages. For ex:
Container(
width: 20.w, //It will take a 20% of screen width
height:30.h //It will take a 30% of screen height
)
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),
],
),
),
),
),
),
);
}
}
I can't figure out what's giving this button extra width/padding. The image used is cropped so has no spacing to the left or right and you can see in the attached screenshot from the dev tools that it doesn't occupy the width. Somehow the button has extra width but I don't know where it's coming from.
I have another identical button next to it and with the added space it's causing overflow.
Changing the image size with the width parameter doesn't affect the amount of space the material button takes up either. It seems to be a fixed size.
This is the whole code:
Scaffold(
body: Row(
children: <Widget>[
MaterialButton(
child: Image.asset("images/male.png", width: 33)
)
],
),
);
I also tried other buttons like FlatButton and RaisedButton but they are the same with this additional width/padding. I also tried setting padding with on the button to EdgeInsets.all(0) but that doesn't change anything either.
The extra space is from the minWidth default value which is taken from the current ButtonTheme (you can see that from the MaterialButton source code). You can remove the extra space by adding minWidth to 0 and padding to 0 to your MaterialButton widget. Something like this:
Scaffold(
body: Row(
children: <Widget>[
MaterialButton(
padding: EdgeInsets.all(0),
minWidth: 0,
child: Image.asset("images/male.png", width: 33),
)
],
),
);
Use a container, where you can specify the width
Scaffold(
body: Row(
children: <Widget>[
new Container(
width: 33,
child : MaterialButton(
child: Image.asset("images/male.png")
),
],
),
);
In Android match_parent and wrap_content are used to resize the widgets automatically relative to their parent to the content the widget contains.
In Flutter it seems by default all widgets are set to wrap_content, how would I change it such that I can fill its width and height to that of its parent?
You can do with little Trick:
Suppose you have requirement of :
( Width,Height )
Wrap_content ,Wrap_content :
//use this as child
Wrap(
children: <Widget>[*your_child*])
Match_parent,Match_parent:
//use this as child
Container(
height: double.infinity,
width: double.infinity,child:*your_child*)
Match_parent,Wrap_content :
//use this as child
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[*your_child*],
);
Wrap_content ,Match_parent:
//use this as child
Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[your_child],
);
In order to get behavior for match_parent and wrap_content we need to
use mainAxisSize property in Row/Column widget, the mainAxisSize
property takes MainAxisSize enum having two values which is
MainAxisSize.min which behaves as wrap_content and MainAxisSize.max
which behaves as match_parent.
Link of the original Article
The short answer is that the parent doesn't have a size until the child has a size.
The way layout works in Flutter is that each widget provides constraints to each of its children, like "you can be up to this wide, you must be this tall, you have to be at least this wide", or whatever (specifically, they get a minimum width, a maximum width, a minimum height, and a maximum height). Each child takes those constraints, does something, and picks a size (width and height) that matches those constraints. Then, once each child has done its thing, the widget can can pick its own size.
Some widgets try to be as big as the parent allows. Some widgets try to be as small as the parent allows. Some widgets try to match a certain "natural" size (e.g. text, images).
Some widgets tell their children they can be any size they want. Some give their children the same constraints that they got from their parent.
There are actually some options available:
You can use SizedBox.expand to make your widget match parents dimensions, or SizedBox(width: double.infinity) to match only the width or SizedBox(heigth: double.infinity) to match only the heigth.
If you want a wrap_content behavior it depends on the parent widget you are using, for example if you put a button on a column it will behave like wrap_content and to use it like match_parent you can wrap the button with a Expanded widget or a sizedbox.
With a ListView the button gets a match_parent behavior and to get a wrap_content behavior you can wrap it with a Flex widget like Row.
Using an Expanded widget makes a child of a Row, Column, or Flex
expand to fill the available space in the main axis (e.g., horizontally for
a Row or vertically for a Column).
https://docs.flutter.io/flutter/widgets/Expanded-class.html
Using a Flexible widget gives a child of a Row, Column, or Flex the flexibility to expand to fill the available space in the main axis (e.g., horizontally for a Row or vertically for a Column), but, unlike Expanded, Flexible does not require the child to fill the available space.
https://docs.flutter.io/flutter/widgets/Flexible-class.html
Use the widget Wrap.
For Column like behavior try:
return Wrap(
direction: Axis.vertical,
spacing: 10,
children: <Widget>[...],);
For Row like behavior try:
return Wrap(
direction: Axis.horizontal,
spacing: 10,
children: <Widget>[...],);
For more information: Wrap (Flutter Widget)
I used this solution, you have to define the height and width of your screen using MediaQuery:
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width
)
To make a child fill its parent, simply wrap it into a FittedBox
FittedBox(
child: Image.asset('foo.png'),
fit: BoxFit.fill,
)
A simple workaround:
If a container has only one top level child, then you can specify alignment property for the child and give it any available value. it'll fill all the space in the container.
Container(color:Colors.white,height:200.0,width:200.0,
child:Container(
color: Colors.yellow,
alignment:Alignment.[any_available_option] // make the yellow child match the parent size
)
)
Another way:
Container(color:Colors.white,height:200.0,width:200.0,
child:Container(
color: Colors.yellow,
constraints: BoxConstraints.expand(height: 100.0), // height will be 100 dip and width will be match parent
)
)
Stack(
children: [
Container(color:Colors.red, height:200.0, width:200.0),
Positioned.fill(
child: Container(color: Colors. yellow),
)
]
),
Use FractionallySizedBox widget.
FractionallySizedBox(
widthFactor: 1.0, // width w.r.t to parent
heightFactor: 1.0, // height w.r.t to parent
child: *Your Child Here*
}
This widget is also very useful when you want to size your child at a fraction of its parent's size.
Example:
If you want the child to occupy 50% width of its parent, provide widthFactor as 0.5
Match Parent
To match or fill the parent (height & width) we can use additional constraints on Container:
Container(
constraints: BoxConstraints.expand(), // ← this guy
child: Text('Center > Container > Text')
)
In Flutter, constraints are the space you can fill (or must fill, if "tight" constraints).
Constraints are given... no actually, imposed by parents.
By default, Container will wrap its content (child:) & size itself to its child, unless overriden (or not allowed by tight constraints).
Using the constraints: argument, we can give Container additional constraints to override default Container constraint behavior (such as wrapping content).
Using Container(constraints: BoxConstraints.something) doesn't overwrite incoming/parent constraints; It just allows us to override default behavior, where allowed, such as wrapping content.
Code Sample - BoxConstraints
Here's a copy/paste code example showing effects of various constraints we can apply to a Container that has "loose" incoming/parental constraints (provided by Center).
import 'package:flutter/material.dart';
class MatchParentPage extends StatefulWidget {
#override
_MatchParentPageState createState() => _MatchParentPageState();
}
class _MatchParentPageState extends State<MatchParentPage> {
BoxConstraints constraints;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Match Parent'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded( // shares space constraint evenly with other Expanded
child: Center( // ← fills tight parent constraint & loosens ↓ child constraint ↓
child: Container( // got loose constraint from Center...
constraints: constraints, // can apply many additional constraints
color: Colors.lightBlueAccent.withOpacity(.3),
child: Text('Center > Container > Text')),
),
),
Expanded(
child: Container(
color: Colors.orangeAccent,
child: Wrap(
children: [
_button('default', null),
_button('*expand()', BoxConstraints.expand()),
_button('*tight(Size.infinite)', BoxConstraints.tight(Size.infinite)),
_button('tight(Size.zero)', BoxConstraints.tight(Size.zero)),
_button('tight(Size.fromHeight(100))', BoxConstraints.tight(Size.fromHeight(100))),
_button('tight(Size.fromWidth(100))', BoxConstraints.tight(Size.fromWidth(100))),
_button('tightForFinite(width: 100, height: 100)', BoxConstraints.tightForFinite(width: 100, height: 100)),
_button('loose(Size.infinite)', BoxConstraints.loose(Size.infinite)),
_button('tightFor(width: double.infinity)', BoxConstraints.tightFor(width: double.infinity)),
_button('tightFor(height: double.infinity)', BoxConstraints.tightFor(height: double.infinity)),
])
),
)
],
),
);
}
Widget _button(String label, BoxConstraints _constraints) {
bool _active = _constraints == constraints;
return Padding(
padding: const EdgeInsets.only(top:8, left: 8),
child: RaisedButton(
color: _active ? Colors.cyanAccent : null,
child: Text(label),
onPressed: () {
setState(() => constraints = _constraints);
},
),
);
}
}
Use this line of codes inside the Column.
For wrap_content : mainAxisSize: MainAxisSize.min
For match_parent : mainAxisSize: MainAxisSize.max
MATCH_PARENT
FractionallySizedBox(
widthFactor: 1.0, // width w.r.t to parent
heightFactor: 1.0, // height w.r.t to parent
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
)
OR
Container(
height: double.infinity,
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
)
OR
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
)
OR
Container(
constraints: BoxConstraints.expand(),
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
)
WRAP_CONTENT
Wrap(children: [
Container(
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
),
])
OR
Container(
constraints: BoxConstraints.tightFor(),
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
)
Match_parent,Wrap_content :
Row(
children: [
Expanded(
child: Container(
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
),
),
])
Wrap_content,Match_parent :
Column(
children: [
Expanded(
child: Container(
child: ElevatedButton(
onPressed: () {},
child: Text("+"),
),
),
),
])
For match parent option you can wrap your widget by a container and give it a width like this
width: double.infinity
this approach will make the widget fill max space available for it on the screen.