Simple Layout Challenge - flutter

I am trying to create a Card that displays the score in a game. The card will be more complex than this example, but in the middle of the card I want to have a Row. That Row contains two Columns. The first Column contains two Rows. In each of those two Rows is the team name and the score.
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Team A'),
Text('100'),
],
),
Row(
children: <Widget>[
Text('Team B'),
Text('100'),
],
),
],
),
Text('15:00'),
],
),
);
}
Now I want t wrap the Text widgets that display the team name in a Expanded. Once I do that I get a long descriptive error that basically says my Row has a parent that does not have a finite width, but it has a child that is asking to expand and these two are in conflict. I get that they are in conflict. How do I fix it? I definitely don't want to give the parent Widget (the Card) a finite width.

It's not 100% clear what you want the scoreboard to look like - this is an instance where a quick mock-up would be a great help.
However, I can tell you why you're getting an exception.
When you wrap your 'Team A' text in an Expanded, during the layout pass flutter gets confused. The top level row has two items, neither of which define any sort of size, so it makes them as large as possible in the cross axis (vertical) and as small as possible in the main axis (horizontal). This means that they delegate their size to their children.
By wrapping 'Team A' in an Expanded, you're basically telling it:
"Hey you, your parent widget is pretty big. I want you to take up all of it that isn't already being used instead of being how big you would normally be."
It responds with
"Sure boss, but I've just checked and none of my parents actually have
a defined size.... so I don't know what to do. Guess I'll just throw an
exception."
The fix is to size the parent, which in this case is the column that contains the two rows (geez, that's confusing).
I think from your description what you want is for that column to take up as much space as possible, so you just have to wrap it in expanded as well. So if you do this, your problem should resolve itself:
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Expanded(child: Text('Team A')),
Text('100'),
],
),
Row(
children: <Widget>[
Expanded(child: Text('Team B')),
Text('100'),
],
),
],
),
),
Text('15:00'),
],
),
);
}
That way, the column containing the two rows expands to take all available space during its layout pass. Once the 'Team A' text gets around to trying its layout pass it can simply size itself (horizontally) to that column.
You can then play around with the flex factor to get everything how you want.
Just an FYI - once you start getting lots of nested containers and rows you might want to consider a CustomMultiChildLayout. It's slightly more manual as you need to do the layout calculations yourself, but it might be easier in the long run depending on how complex your scoreboard is.

Related

Restricting the size of widgets programmatically

I'd like to create the layout shown in the picture below. This shows up fine:
Column( Row, ListView()))
While this renders errors:
Column( Row, Row( ListView(), Column ))
As soon as I replace the inner ListView() with Row( ListView(), Column ), nothing shows up and I get varying error messages, based on various changes I did.
Most often, I see a 'viewport has unlimited horizontal size'.
I guess Listview to be the source of the problem. That said, I'm no aware how to fix it.
Furthermore, I did find various hints on SO, but none fixed the problem.
How do I fix the problem?
Update 1
While the right Column() may be fixed width, the ListView() should take all remaining space of the screen [= availableWidth - WidhtOf(Column())].
put the ListView inside a Container And give it height/width
code
Column(
children: [
Row(
children: [],
),
Row(
children: [
Container(
height: 100,
width: 100,
child: ListView(
children: [
Text(
"1 C/G")
],
)),
Column(
children: [],
)
],
)
],
),
Both Row and Column have a mainAxisSizeparameter where you can set it to MainAxisSize.min so they will take their children's size.
Now, about the ListView, there is a shrinkWrap parameter that makes it so its max size will be its children's size. Another idea might be using a Flexible widget around the ListView, it does almost the same as Expanded, but the difference is: Expanded forces the child to occupy the remaining space, while Flexible allows the children to have any given size smaller than that, and that's because of its fit parameter that by default is FlexFit.loose.

How to display text on top of containers?

I'm creating a card game and my UI has a Column() with various combinations of children widgets. One child is a Row
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(),
Container(),
],
),
The Container() widgets each display what looks like playing cards. They have BoxDecorationsto provide a border + a child that's a Row(). The Row's children display a number (text) and a suit (image).
I would like to flash text over the two playing cards, something that's visible for just a couple seconds, e.g., "Well done." Although I'm always open to help, I assume this will amount to (Rich)Text() widgets and some sort of timer or Duration object, and that I can figure it out.
What I'm struggling with is figuring out how to create a text overlay that is both on top of and spans the two images. Do I have to do something like wrap the higher level Row() object in a Stack()? I hate all that nesting, plus I don't know how to have the text always be on top of everything else.
I think you are looking for Overlay widget:
Scaffold(
body: Container(child: Overlay(initialEntries: [
OverlayEntry(builder: (_) => Image.network("https://source.unsplash.com/random", fit: BoxFit.cover,)),
OverlayEntry(builder: (_) => Center(child: Text("Overlay", style: TextStyle(color: Colors.yellow),))),
])),
),
For now I am going with (pseudocode):
Stack(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(),
Container(),
],
),
Center(
child: Text(),
),
],
),
With the Row() centered and the child:Text() centered, everything lines up and looks good. I do, however, think I need to spend time and learn about Positioned() widgets. I think that will ultimately be a better solution.

How do I place a widget in the center of the screen while ignoring column?

There is a widget (for example text). In the real example it comes after another widget in Column. How to put it in the center of the screen? As I understand it, the Column does not. How can this be done? Or are there other ways to specify the location of items?
Column takes all the available space of the parent and by default aligns the items to start
You can change fix this changes the MainAxisAlignment to center.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("test"),
],
),
or changing parents height and move it to the center (be careful if you add many items it will cause overflow)
child: Center(
child: Container(
height: 25,
child: Column(
children: <Widget>[
Text("test"),
],
),
),
),
Using Stack widget you can do it:
var textEmpty = Text("Some Text");
Stack(children: <Widget>[
Column(children: <Widget>[
Container(
,margin: EdgeInsets.only(top: 100.0)
,child: Center(child: textEmpty, ),)],),
listView,
]);
Use Center Wigdet instead of Container. Hope this helps!
No worries where it comes from. It depends on the view where you have added it.
If you want to show in the center then just use Center widget
If you want to use Container widget then, you have to add the width and height both to double.infinite
If you want to use it in the same widget then you can use the Stack widget as well.
It totally depends on the requirement of the view and in which way you are tried to implement it.

Text inside nested Row is not wrapping unless enclosing column is wrapped in Flexible widget

The text inside my Text widget is not wrapping, even when wrapped in a Flexible widget. I found a workaround but it involves wrapping a Column widget inside a Flexible widget which I am not sure is best practice.
I have seen many similar answers online, but none for my exact problem. It is relevant that my text widget in question is nested inside the following hierachy Row -> Column -> Row
A rough idea of how this layout should look:
Here is the original code. When run the text overflows the screen rather than wrapping.
Row(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("top column"),
Row(
children: [
Text("left row"),
Text(
"this is the text that needs to wrap, this is the text that needs to wrap",
),
],
),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
Text("right side"),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
);
Here is code that solves my problem, but it involves wrapping both the Text widget AND the outer Column widget in a Flexible widget:
Row(
children: [
Flexible( // added Flexible widget, but doesn't seem correct.
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("top column"),
Row(
children: [
Text("left row"),
Flexible( // added Flexible widget
child: Text(
"this is the text that needs to wrap, this is the text that needs to wrap",
),
)
],
),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
),
Text("right side"),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
);
Is there a better way to achieve this wrapping or is enclosing the Column in a Flexible widget a satisfactory approach?
Your approach is right, here's why :
You managed to the problem of the inner Row long text by wrap it into a Flexible,to prevent an overflow in the horizontal axis.This works perfectly if this is your whole tree,but this isn't the case.
Up on your widget tree there is no horizontal constraint limiting the width of children.Column wraps in the vertical axis, so the children of the upper-most Row one with fixed width (Text("right side")) and the other is unbounded one (Column).
When you wrapped the Column with Flexible, it made children bounded horizontally. Another approach would be to use a Container, but it is not the best one as it needs you to give the width before build.

What would be a good way for a widget to take 1/3 of the screen?

I am trying to make a widget take up 1/3 the screen regardless of the phone orientation. How do I accomplish this?
I got what I wanted by using a Column and wrapping the contents in Flexible and changing the flex to get the child to take up 1/3 the screen:
new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Flexible(flex: 2, child: new SizedBox()),
new Flexible(flex: 1, child: createTextBox())
]
);
There's a few widgets that might help: AspectRatio, Column/Expanded, and CustomSingleChildLayout in particular. It's hard to give a good answer without knowing exactly what you want to do though.
You should try using Expanded widgets as the children of your row, like so:
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
new Expanded(
child: yourFirstContentWidget,
),
new Expanded(
child: yourNextContentWidget,
),
new Expanded(
child: yourLastContentWidget,
)
]
)
The Expanded widgets will each try to fill as much space of the row as possible. Since there are three of them, they will have to share the space and you'll end up with each taking up 1/3 of the row.