Trying to Understand ConstrainedBox - flutter

Flutter Newbie here.
Please refer to this example from Flutter docs Example
I have added just 4 lines to that code. You will find those lines which has comment "Lines Added by me".
But there is no change in the output.
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.bodyText2,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
print(viewportConstraints.minWidth); // Lines Added By me
print(viewportConstraints.maxWidth); // Lines Added By me
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
maxWidth : viewportConstraints.minWidth/2, // Lines Added By me
minWidth : viewportConstraints.minWidth/2, // Lines Added By me
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
// A fixed-height child.
color: const Color(0xffeeee00), // Yellow
height: 120.0,
alignment: Alignment.center,
child: const Text('Fixed Height Content'),
),
Container(
// Another fixed-height child.
color: const Color(0xff008000), // Green
height: 120.0,
alignment: Alignment.center,
child: const Text('Fixed Height Content'),
),
],
),
),
);
},
),
);
}
So I am just playing with that example. And i wanted to see that if i can control the width of containers by providing maxWidth to constrainedBox. But its not working and i want to know reason/concept behind that?
Now in section layout behaviour of container says that "If the widget has an alignment, and the parent provides bounded constraints, then the Container tries to expand to fit the parent, and then positions the child within itself as per the alignment."
Can someone explain me where i am wrong in my understanding ?
From the above ScreenShot it seems that proper(width/2) constraints are not passed to column.

Related

Flutter - Size missing error on TextField

When using TextField inside a container that is centered by a parent row widget. No parents have a defined width. Container is supposed to take width of the content inside it.
Error - parentData: offset=Offset(0.0, 0.0); id=_ScaffoldSlot.body (can use size) constraints: BoxConstraints(0.0<=w<=1920.0, 0.0<=h<=924.0) size: MISSING behavior: opaque
My stateless widget:
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Pangea')),
body: Container(
color: Colors.black45,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: double.infinity,
// width: double.infinity,
child: Column(
children: [
TextField(
// decoration: InputDecoration(
// constraints: BoxConstraints.tightFor(
// width: 300,
// ),
// ),
),
],
)),
],
),
),
),
);
}
If I use InputDecoration inside TextField it works fine. But I would like to keep its size dynamic. May be perhaps I can specify min and max width .
I think you might need IntrinsicHeight widget.
It makes its child to wait until its parent has a defined Height and, with that value in its hands, builds the child.

How to fix a button at bottom of a single child scrollview with a list

I have a SingleChildScrollView and inside it I have a list with some cards, that you can remove ou add more. I need to fix an add button at the bottom of the screen, when the card list is not scrollable yet, but when the card list increase size and the scrollview is able to scroll now (to see all the content), the button must follow the list and not keep fixed at the bottom anymore.
For now, what I did to solve this, was check the scroll view every time that a card is added ou removed, if I checked that the screen is now scrollable or not scrollable I change some properties of my build widget:
SingleChildScrollView(
controller: _scrollController,
physics: AlwaysScrollableScrollPhysics(),
child: Container(
height: isNotScrollable
? _pageSize - (_appBarSize + _notifySize)
: null,
padding: const EdgeInsets.symmetric(
horizontal: Constraints.paddingNormal),
child: Column(
.....
and after the list render I create the button like this
isNotScrollable
? Expanded(
child: Container(),
)
: Container(),
CVButton(
color: Palette.white,
Basically, my idea is: if the screen is not scrollable yet (the list content fits in the screen size) I will set a height to the container inside scrollview and add a Expanded() widget before the add button (so the button will stay in the bottom of the container), but if the screen is scrollable (the list content not fits inside the screen size) so I remove the container height and the Expanded widget, then the button will follow the list now as normally.
I don't know if this is the better way to deal with that, I want to know if there is some way to do this without this 'dinamic' way that I am doing, only with fixed widgets and not changing the widget according to the state of the scrollview.
An example when the list becomes scrollable and the button will keep at list bottom
Here the list is not scrollable yet but the button must be at the screen bottom and not list bottom
(I dont wanna use bottomNavBar)
Anyone has any idea how I can solve this?
I have a solution for this. check the code bellow. I added some buttons to add or remove cards. The main trick is to use constraints like minHeight.
import 'package:flutter/material.dart';
class BottomButton extends StatefulWidget {
#override
_BottomButtonState createState() => _BottomButtonState();
}
class _BottomButtonState extends State<BottomButton> {
List<Widget> cards = [];
#override
Widget build(BuildContext context) {
var appBar2 = AppBar(
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
_addCard();
}),
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
_removeCard();
}),
],
);
return Scaffold(
appBar: appBar2,
body: Container(
height: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top + appBar2.preferredSize.height),
alignment: Alignment.topCenter,
child: ListView(
primary: true,
children: [
Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top +
appBar2.preferredSize.height),
),
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top +
appBar2.preferredSize.height +
50),
),
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: cards,
),
),
ElevatedButton(
child: Text('this is a button'),
onPressed: () {},
),
],
),
)
],
),
),
);
}
void _addCard() {
Widget card = Card(
child: Container(
height: 100,
color: Colors.red,
padding: EdgeInsets.all(2),
),
);
setState(() {
cards.add(card);
});
}
void _removeCard() {
setState(() {
cards.removeLast();
});
}
}

Fix minimum width to a Widget which needs to expand in Flutter

I need to fix a minimum width to my Column Widgets. Inside each of them, I have Text Widgets which can be very short or very long. I need to fix a minimum width to them in order to have an acceptable size of Column even if the text is short. The other Column need obviously to adapt himself.
Row(children: [
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 80), // do not work
child: Text("short text"),
),
],
),
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 110), // do not work
child: RichText(
text: TextSpan(
text:"very very longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg text")),
),
],
),
],
)
There's probably a dozen ways to do what you want. And likely none of them straightforward or easy to understand. (The subject of constraints & sizes is quite complicated. See this constraints page for more examples & explanations.)
Here's one potential solution.
This will set a minimum width for the blue column (based on stepWidth), but will expand/grow if the text (child) inside wants to.
The yellow column will resize to accommodate the blue column.
class ExpandedRowPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Expanded Row Page'),
),
body: SafeArea(
child: Center(
child: Row(
children: [
IntrinsicWidth(
stepWidth: 100,
// BLUE Column
child: Container(
color: Colors.lightBlueAccent,
child: Column(
children: [
//Text('Short'),
Text('shrt')
],
)
),
),
// YELLOW Column
Flexible(
child: Container(
alignment: Alignment.center,
color: Colors.yellow,
child: Column(
children: [
Text('Very lonnnnnnnnnnnnnnnnnnnnnnnnnnnng texttttttttttttt'),
],
)
),
)
],
)
),
),
);
}
}
You could do the above without a Flexible yellow column, but a very long text child would cause an Overflow warning without a Flexible or Expanded wrapping widget.
A Row widget by itself has an infinite width constraint. So if a child wants to be bigger than screen width, it can, and will cause an overflow. (Try removing Flexible above and rebuild to see.)
Flexible and Expanded, used only inside Row & Column (or Flex, their superclass), checks screen width and other widgets inside a Row, and provides its children with a defined constraint size instead of infinite. Children (inside Flexible/Expanded) can now look up to parent for a constraint and size themselves accordingly.
A Text widget for example, will wrap its text when it's too wide for constraints given by Flexible/Expanded.
use FittedBox();
suppose Example:
Row(
children: [
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 80), // do not work
child: Text("short text"),
),
],
),
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 110), // do not work
child:
FittedBox(
child: RichText(
text: TextSpan(
text:
"very very longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg text")),
),
),
],
),
],
);

Wrapping contents of Row widget if overflowed

I have a list storing the asset image location. Using that list I display those many cards of certain width side by side inside a website.
When the items in list increases, the row overflows. I want those cards to appear below, To have rest of cards in another line.
I'm returning this row to a singleChildScrollView parent widget.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: ProjectData.map((item) => Container(
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
child: Card(
child: Image.asset(item.fileLoc),
),
)).toList(),
),
This row sits under a Column() widget and this entire Column is returned to a SingleChildScrollView()
Use the Wrap widget:
A widget that displays its children in multiple horizontal or vertical runs.
In your case, you can use as follows:
Wrap(
spacing: 8.0, // gap between adjacent cards
runSpacing: 4.0, // gap between lines
children: ProjectData.map((item) => Container(
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
child: Card(
child: Image.asset(item.fileLoc),
),
)).toList(),
)
You can use GridView class, since, you can keep count of the item you want to show in per row. Basically GridView.count() is the one for you.
To control the number of items in it, the power will be given by crossAxisCount
Do not forget to give height to the GridView(), else, you will see a lot of errors coming up. It needs a parent height
We are passing the item as arrayname.map((item) => Your_Widget()).toList().cast<Widget>()
I have used my myArray for dummy show purpose. Feel free to use yours. The idea is to give you the best possible option
In case you wanna explore SingleChildScrollView class it will come in handy
LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
child: GridView.count(
crossAxisCount: 2, // here you keep track of count
children: myArray.map((item) => Container(
margin: EdgeInsets.only(left: 15.0, right: 15.0, top: 15.0),
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(15.0)
)
)).toList().cast<Widget>()
)
)
)
);
}
)
Result
Feel free to try it out.

The equivalent of wrap_content and match_parent in flutter?

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.