Flutter card layout with column - flutter

I have a simple card design, my card is wrapped with a container with a specific height and width, so the children are bounded
The card has a picture from Image.network and a text at the bottom, both occupying half the height of the card
However, my code receives this error:
Error: Cannot hit test a render box that has never been laid out.
From what I know, constraints goes down the tree and children return the size to the parent. I know the column would not pass constraints to the children, however, the two expanded widget SHOULD know to take up half of the card's height?
My goal is to understand WHY this doesn't work, moreover than the actual working code. thank you!
Code:
var height = MediaQuery.of(context).size.height;
return Container(
height: height * 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: FittedBox(
child: Image.network('https://source.unsplash.com/random'),
),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
);

The problem is that FittedBox tries to scale its child to fit accordingly; thus the child needs to be laid out before. Because the child is an image being loaded in this example, it's not laid before the fitting. To overcome this issue, you can use a SizedBox.expand instead of the FittedBox:
Container(
height: height * 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox.expand(
child: Image.network('https://source.unsplash.com/random'),
),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
)

-Don't use MediaQuery inside the build Methode, before the return.
-Avoid redundant argument: https://dart-lang.github.io/linter/lints/avoid_redundant_argument_values.html for the flex: 1
Use fit: BoxFit.contain
return Container(
height: MediaQuery.of(context).size.height* 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: Image.network('https://source.unsplash.com/random',fit: BoxFit.contain),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
);

Related

how can i set the size of the child inside the row?

I want to make children use flexible sizes
how can i set the size of the child inside the row? can you guys help me? and find the error constraint
anyone help me
return SafeArea(
child: Column(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
color: Colors.lightBlue,
child: Row(
children: [
Expanded(
flex: 5,
child: Container(
color: Colors.green,
),
),
Expanded(
flex: 5,
child: Container(
color: Colors.blue,
),
)
],
),
)
],
),
)
],
),
);
To place children in a row you can use Flexible widget. The value of flex defines how much space each child should take. In the example that you provided.. Both children will be equally spaced since you gave flex value 5 for both.
return Scaffold(
body: Center(
child: SizedBox(
height: 50,
child: Row(
children: [
Flexible(
flex: 5,
child: Container(
color: Colors.red,
)),
Flexible(
flex: 5,
child: Container(
color: Colors.green,
)),
],
),
),
),
);
If you change the flex value to 1 and 5.. Now the total divisions will be 6 and out of that the first child will take 1 part and the second child will take 5 parts like below
return Scaffold(
body: Center(
child: SizedBox(
height: 50,
child: Row(
children: [
Flexible(
flex: 1,
child: Container(
color: Colors.red,
)),
Flexible(
flex: 5,
child: Container(
color: Colors.green,
)),
],
),
),
),
);
Also if you use a singleChildScrollView and an expanded inside that it then the expanded widget has no bounds so it will throw an error. So wrapping the Expanded with a SizedBox with a specific width will give you expected results.
EDIT
To have a SingleChildScrollView with dynamic content in the row you can do this
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize : MainAxisSize.min,
children:[
SizedBox(
width: 100,//this is important since its a row and the row will take full width and throw an error.
child: Image.asset(""),
),
//Another SizedBox or Text or any widget..
]
)
)

Flutter align widget horizontaly and verticaly

I am working on a Flutter project. I'm trying to create a widget like the image below and I can't manage to do it.
So what I'm trying to do, is to create a widget with a Text at the top left (the size of this widget is not constant and depends on the text in it).
I'm trying to align 2 other widgets with the Text:
The Widget 1 is aligned horizontaly and centered with the Text widget and at its right with no space between
The Widget 2's right edge is aligned verticaly with the Text widget's right edge, and is positioned under it (and there is no space between the 2 widgets).
I tried to implement this with Column and Row widgets or with a GridView but I couldn't manage to do so.
Also the overall size of this custom widget has to be shrinked.
Does someone know how to do something like that ?
Thank you.
This works
Container(
width: double.infinity,
height: 300,
color: Colors.grey[200],
child: Column(
children: <Widget>[
Row(
children: [
Flexible(
flex: 2,
child: Container(
height: 200,
color: Colors.blue,
),
),
Flexible(
flex: 1,
child: Container(
height: 100,
color: Colors.green,
),
),
],
),
Row(
children: <Widget>[
Flexible(
flex: 2,
child: Align(
alignment: Alignment.centerRight,
child: Container(
width: 150,
height: 100,
color: Colors.orange,
),
),
),
Flexible(
flex: 1,
child: Container(),
),
],
),
],
),
),

Word-wrapping do not working with long text

Here is my widget:
return Card(
child: Container(
height: 150,
child: Row(
children: <Widget>[
Placeholder(
fallbackHeight: 100,
fallbackWidth: 100,
),
Container(
width: _deviceHeight,
color: Colors.red,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Text(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasssssaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
overflow: TextOverflow.ellipsis,
maxLines: 4,
)),
// Expanded(flex: 8, child: Text("bbb")),
Flexible(child: Text("bbb")),
// Flexible(child: Text("aaa")),
],
),
)
],
),
));
I expected that text will be placed on new line, but I am getting overflow:
Wrap your inner Container with an Expanded to provide it with the available parent constraints, otherwise the Container has infinite height and will result in an overflow (because you are on landscape).
Expanded(
child: Container(
width: _deviceHeight,
color: Colors.red,
child: Column(
...
You could wrap the second element of the Row with Flexible, like this:
Row(
children: <Widget>[
Placeholder(..),
Flexible(
child: Container(
color: Colors.red,
child: Column(...
Inside the Row you need Placeholder to keep its width and Container width to be flexible.
Edit: And you should remove the width of the Container, since it would be flexible and it should take as much as possible to fit the screen.

Flutter - How create Container with width match parent?

I use this simple widget in my Flutter app:
FlatButton(
child: Column(
children: <Widget>[
Image.asset(assets, height: 40, width: 40),
Text('Title'),
//my color line
Container(
height: 5,
width: ?,
color: Colors.blue[800],
)
],
)
)
I need color line (in buttom), with width match parent. but I don’t know how to do it.
Container(
height: 5,
width: double.infinity,
color: Colors.blue[800],
)
Use IntrinsicWidth
FlatButton(
child: IntrinsicWidth(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Image.asset(assets, height: 40, width: 40),
Text('Title'),
//my color line
Container(
height: 5,
width: ?,
color: Colors.blue[800],
)
],
))
)
Some cheatsheet
Two ways of doing this
ConstrainedBox:
It Creates a widget that imposes additional constraints on its child, which internally uses SingleChildRenderObjectWidget to add constraints over the child widget.
ConstrainedBox(
constraints:
const BoxConstraints(minWidth: double.infinity, maxHeight: 10),
child: Container(
color: Colors.blue,
),
),
SizedBox:
SizedBox simply creates a box with a given width/height and doesn't allow the child to go beyond given dimensions. It also used SingleChildRenderObjectWidget to decide the child render area
SizedBox(
width: double.infinity,
height: 5,
// height: double.infinity,
child: Container(
color: Colors.blue,
),
),
Container in a Column with no child will always expand. if you do add a child it will wrap it.
Also if you don't constrain your Column in the button it will also grow to full available height.
My suggestion to you is instead of putting an empty Container in your button's Column, rather wrap the button with a Container and give it a bottom border. This will give you your colored line on the bottom.
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 5, color: Colors.blue[800]),
),
),
child: FlatButton(
onPressed: () {},
child: Column(
mainAxisSize: MainAxisSize.min, // prevent Column to expand full height
children: <Widget>[
Icon(Icons.cake, size: 40),
Text('title'),
],
),
),
),

Using FractionallySizedBox in a Row

I want to have a widget of arbitrary width that contains a row of three widgets sized relatively to its parent.
FractionallySizedBox sounds like the right tool for the job, so I tried it like this:
Container(height: 24.0, color: Colors.black, child:
Row(children: [
FractionallySizedBox(heightFactor: 1, widthFactor: 0.25,
child: Container(color: Colors.orange)),
FractionallySizedBox(heightFactor: 1, widthFactor: 0.15,
child: Container(color: Colors.green)),
FractionallySizedBox(heightFactor: 1, widthFactor: 0.05,
child: Container(color: Colors.blue)),
]
)
But I'm getting an error
BoxConstraints forces an infinite width.
This happens even when I set some width on the enclosing container.
Why doesn't this work?
As explained in the "FractionallySizedBox (Flutter Widget of the Week)" video by the Flutter team, the FractionallySizedBox needs to be wrapped in Flexible widget, "so it plays well with a row or a column".
Reference: https://www.youtube.com/watch?v=PEsY654EGZ0
This should be the solution to the original question, formatted below:
Container(
height: 24.0,
color: Colors.black,
child: Row(
children: [
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.25,
child: Container(color: Colors.orange),
),
),
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.15,
child: Container(color: Colors.green)),
),
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.05,
child: Container(color: Colors.blue)
),
),
]
)
)
I have also worked on something similar and was looking for a solution to the problem, this is my code for representing data levels:
Row(
children: <Widget>[
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
],
),
Resulting in:
evenly spaced bars
You can't use FractionallySizedBox in Row or Column directly. You can split as percent Row and Column use flex parameter of Expand widget like below.
Row(
children: <Widget>[
Expanded(
flex: 6, // 60% of space => (6/(6 + 4))
child: Container(
color: Colors.red,
),
),
Expanded(
flex: 4, // 40% of space
child: Container(
color: Colors.purple,
),
),
],
),
FractionallySizedBox seems to only work outside of Row, Column, and Wrap widgets. I would recommend this approach instead:
class FractionalPositioningQuestion extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Replace with the size your parent widget is.
final pWidth = MediaQuery.of(context).size.width;
return Row(children: <Widget>[
Container(
width: pWidth * 0.25,
color: Colors.orange,
),
Container(
width: pWidth * 0.15,
color: Colors.green,
),
Container(
width: pWidth * 0.05,
color: Colors.blue,
),
],
mainAxisAlignment: MainAxisAlignment.center, // Depending on what you want
);
}
}
You still need to find a way to obtain the arbitrary width of the parent widget though.
Hope this helps a little at least :-)
FractionallySizedBox refused to work within Rows.
Much easier to use the flex field of Expanded.
import 'package:flutter/material.dart';
myRow() => Row(children: [
Expanded(
flex: 8,
child: Container(
height: 100,
color: Colors.green,
),
),
Expanded(
flex: 2,
child: Container(
height: 100,
color: Colors.red,
),
),
]);
content() => Column(children: [
Text(" ----Start ----"),
myRow(),
Text(" ----End ----"),
]);
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
title: 'Flutter Playground',
home: Material(
child: SafeArea(child: Container(width: 80, child: content()))));
}
FractionallySizedBox doesn't work as expected in rows and columns, even wrapped in a Flexible widget.
tldr: It seems like the heightFactor (for columns) is applied not to the box itself, but to the column overall. This is a neat effect, but not what is expected
Details:
I made a DartPad that lets you see it.
OrangeBoxes have heighFactor=0.5 (you can change it in the code)
BlueBoxes are fixed at 20px by 20px.
Problem1
Expected Behavior: Two OrangeBoxes take up the whole screen. Half each.
Actual Behavior: Two OrangeBoxes each take up 1/4.
Problem2
Expected Behavior: One OrangeBox takes up half the screen. Adding BlueBoxes doesn't make the Orange Box smaller.
Actual Behavior: Adding BlueBoxes makes the OrangeBox smaller. So that the whole column takes up half the screen. Then, if many Blue Boxes added, the whole column eventually grows.
Photos
I also tested this on Android, and I get the same rendering behavior from a quick-and-dirty check.
Extra Notes
The gist is here if someday the dartpad link breaks
https://gist.github.com/chonghorizons/0d97a4202a0cd8838aa25713f1c19c13