Flutter Layouts, expand Container in Column to match other widgets width - flutter

I am creating chat bubbles, I want the width to be defined by the message itself, but then I added a "header" (a Row) with the name of the sender and a decorative line (a Container).
I want this line to fill the available space but when I expand it it pushes the width of the bubble to its maximum regardles of the width of the message. In this sceenshots you can see that the recieved messages are too wide for its content.
This is a simplified version of the code, If I don't put the expanded the red Container is invisible (width 0), with the expanded takes the maximum space available but I want column to remain of the width that determines the message.
Thanks a lot.
return Container(
color: Colors.grey.shade200,
constraints:
BoxConstraints(maxWidth: (MediaQuery.of(context).size.width * 0.7)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("Juan"),
Expanded(
child: Container(
color: Colors.red,
height: 3,
),
)
],
),
Text("Text of the message"),
],
));

pskink nailed it. His answer in the comment:
you need: child: IntrinsicWidth(child: Column(... –
pskink
Hopes this also helps somebody else.

Related

Flutter: how to center a child, but set a limit on how big it can grow on one side of the margin

I have a home/login screen which is made up of a column that fills the entire screen like so:
Column(
children: <Widget>[
Expanded(
child: Container(
child: Logo(),
),
),
showThis ? This() : That(),
],
),
The second child of the column is dynamic and can have different heights, and this screen will have inputs so the keyboard will also affect the height.
I want to center Logo() vertically within the container when it's small (e.g. when keyboard is active), but limit how much the 'top margin' is able to grow, so that when the keyboard is hidden and This()/That() is small enough, Logo() will be in a static position on the screen, say 150 from the top (no longer centred vertically).
One method I have tried was using 2 empty Expanded() above and below Logo() and wrapping the top part in a ConstraintedBox(), but I am not able to get it to behave correctly.
Have you tried with Center() and also if you wanna in a specific position use Stack, for example
Stack(
children: <Widget>[
showThis(),
Positioned(top: 150, child: Logo())
],
)
This is what ended up working for me:
Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
// constrain top spacing with a max height
Flexible(child: Container(height: 160)),
_logo(),
Spacer(),
],
),
),
_bottomWidget(),
],
),

How to make part of flutter widget behave like a stack

What I am Implying to do is that if there is a Column and two containers in it. I want another widget pinned to to the first container which overlaps parts of the 2nd container and move with the first column (if i apply some special scroll effects).
Here is dummy code.
Column(
children: <Widget>[
Container(
height: 300,
child: <Some child widget which is pinned to this box and overlaps the second box>
),
Container(
height: 300,
)
],
)
If I have understood your question properly one way is to use two stack's itself like as follows.
Stack(
children: <Widget>[
Container(
height: 300,
margin: EdgeInsets.all(300),
),
Stack(children: <Widget>[SizedBox(height: 300,),TheWidgetYouWantToOverlap()],)
],
)

How can I fill whole free place in a column with a children widget with required size property?

I have an UI in Flutter that has a simple circular chart with some text.
This circular chart (https://pub.dev/packages/flutter_circular_chart) has a required size property. So, we must define its size, but in this way it won't be responsive for all UI screen sizes, it does not fit the whole space I'd like to.
So, e.g. I'd like to set its size to also cover the space annotated with blue:
Is it possible to set its size dynamically to fit whole free space in the column?
I tried expaned, fittedbox with double.infinity size parames etc. but neither of them helped.
My code:
Widget _buildChart() {
return Expanded(
child: Container(
color: Colors.green,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
AnimatedCircularChart(
duration: Duration(milliseconds: 0),
key: _chartKey,
size: const Size(140.0, 140.0),
chartType: CircularChartType.Radial,
percentageValues: true,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildText(
'${AppLocalizations.of(context).localize("free")}: $freeCount',
Colors.white),
_buildText(
'${AppLocalizations.of(context).localize("busy")}: $busyCount',
Theme.of(context).buttonColor),
],
)
],
),
),
);
}
Would you be so kind to help me to solve this? How can I make its size responsive? Is it possible at all?
Wrap your AnimatedCircularChart in FittedBox which is further wrapped in Expanded and you don't have to deal with Size anymore. No matter what size you provide, it will always give you the maximum space left.
Expanded( // add this
child: FittedBox( // and add this
child: AnimatedCircularChart(
key: Key("chart_key"),
duration: Duration(milliseconds: 0),
size: Size(1, 1), // no matter even if it is 1, it will take up all remaining space
chartType: CircularChartType.Radial,
percentageValues: true,
initialChartData: data,
),
),
)
Full code:
Widget _buildChart() {
return Expanded(
child: Container(
color: Colors.green,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded( // add this
child: FittedBox( // and add this
child: AnimatedCircularChart(
key: Key("chart_key"),
duration: Duration(milliseconds: 0),
size: Size(1, 1), // no matter even if it is 1, it will take up all remaining space
chartType: CircularChartType.Radial,
percentageValues: true,
initialChartData: data,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text("Free: 14"),
Text("Busy: 31"),
],
)
],
),
),
);
}
Update:
If you are talking about the remaining space above the ring on bigger screen, then it is because of the ring ability to remain circular in shape, so it can't really go beyond that. I have created a small video recording to show you the effect. And I have used size: 1.
Use the LayoutBuilder class for that.
The layoutbuilder class allows you to give a defined height depending on the parent widget constraints. For example you could say that the chart height is 40% of the available height of the parant widget.
For further responsiveness (screen rotation etc.) check out MediaQuery

Alignment of widgets ignored in row

I'm creating a new widget that displays some bubbles, that should be connected by lines.
5-------6
|
4-------3
|
1-------2
I chose a ListView as the outer widget, if I understood it correctly this should give me the scrollability:
return Expanded(
child: ListView(
children: _createLWItems(_createBubbles()),
),
);
Children is a List of my inner widget. Each widget is a row, so I can place two bubbles next to each other:
return Container(
child: Row(
children: <Widget>[
Container(
child: bubbleItem1,
alignment: Alignment.centerLeft,
),
Container(
child: bubbleItem2,
alignment: Alignment.centerRight,
)
],
),
decoration: BoxDecoration( //TODO remove after testing
shape: BoxShape.rectangle,
border: Border.all(color: Colors.blue),
),
);
BubbleItem is only a Container with a BoxDecoration wrapped with a GestureDetector.
What I wanted to achieve is, that the left bubble is aligned to the left, and the right to the right, but instead, both are placed next to each other to the left, although row takes the entire width of my screen, as you can see (blue border):
As I read here there is a MainAxisAlignment for the row-widget but this would just align all childs left, right, centered, etc., not what I have in mind.
How can I place those bubbles the way I intend to?
If they are placed correctly I want to connect them with lines. Is there a way to do this without calculation? My idea would be to insert another widget between the bubbles that takes a line, but I'd need to tell it somehow, that it should take the entire space between the bubbles of his row.
At the moment I'm a bit unsure if I should calculate anything because I read something about 'you cannot calculate before it is rendered', but on the other hand I'm missing some understanding how to do it in flutter without it.
Alignment does work for Row children, but not in the way that you are expecting. If for example you had a Row child with half the height of the Row, you could use Alignment to specify if you want that child at the top or the bottom of the Row - the default is centered.
By default, the children are positioned sequentially within a Row (horizontally) as well as within a Column (vertically), which is what you've got in the screenshot you attached. If you simply wanted to position the 2 circles at the left and right ends of the Row, you could use a MainAxisAlignment value of spaceBetween, which places the free space evenly between the children.
If you want to also draw a line between the circles however, then you will not have any space left to distribute within the Row, so the MainAxisAlignment will not help. What you want instead is an Expanded widget within the Row, between the circles. This will create a widget that takes up all the remaining space within the Row, which is where you want your line to be.
Row(
children: <Widget>[
Container(
child: bubbleItem1,
alignment: Alignment.centerLeft,
),
Expanded(
child: Divider(),
),
Container(
child: bubbleItem2,
alignment: Alignment.centerRight,
)
],
)
You are giving the Alignment to the container's item, not the container itself
Try giving the Alignment to the Row
Container getContainer(){
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: 50,
width: 50,
child: Center(child: Text("Left")),
),
Container(
height: 50,
width: 50,
child: Center(child: Text("Right")),
)
],
),
decoration: BoxDecoration( //TODO remove after testing
shape: BoxShape.rectangle,
border: Border.all(color: Colors.blue),
),
);
}

Make container widget fill parent vertically

TL;DR Need the container to fill the vertical space so that it can act as a ontap listener. Have tried most solutions but nothing seems to work.
So what I am trying to do is to make my container fill up the vertical space while still having a fixed width. Two first is what I have and third is what I want. The idea is to have the container transparent with a gesture ontap listener. If anyone have a better idea as for a different solution, feel free to suggest.
Widget build(BuildContext context) {
return new GestureDetector(
onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _handleDragEnd,
child: new Stack(
children: <Widget>[
new Positioned.fill(
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Container(
child: new IconButton(
padding: new EdgeInsets.only(top: 16.0, bottom: 16.0, left: 24.0, right: 24.0),
icon: new Icon(Icons.warning),
color: Colors.black12,
onPressed: () {},
)
),
],
),
),
new SlideTransition(
position: new Tween<Offset>(
begin: Offset(0.0, 0.0),
end: const Offset(-0.6, 0.0),
).animate(_animation),
child: new Card(
child: new Row(
children: <Widget>[
new Container(
width: 20.0,
height: 20.0,
color: Colors.amber,
),
new Expanded(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_getListTile(),
_ifStoplineIsToBeShown()
],
),
)
],
)
),
),
],
)
);
}
I am quite sure that i have been missing something considering the fact that I have tried a lot of different things and nothing seems to work.
I have also uploaded an image with the debug painting here.
PS. I know I have set the height to a fixed value, but this is the only way to show the container.
The trick is to combine an IntrinsicHeight widget and a Row with crossAxisAlignment: CrossAxisAlignment.stretch
This force the children of Row to expand vertically, but Row will take the least amount of vertical space possible.
Card(
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
width: 20.0,
color: Colors.amber,
),
// Expanded(...)
],
),
)
)
To stretch the container to full height of the parent use property constraints:BoxConstraints.expand() in container widget. Container occupy the complete space independent of the of child widget
Container(
color: Colors.green,
child: Text("Flutter"),
constraints: BoxConstraints.expand(),
)
Please refer the link Container Cheat sheet for more about container
Simply pass in: double.infinity.
If you want a Container to fill all available space, you can just pass in:
width: double.infinity,
height: double.infinity
Explanation:
In Flutter, a child widget cannot exceed the "layout constraints" imposed by its parent widget. During the layout phase, Flutter engine uses a constraint solver to automatically correct "out-of-bound" values into what's allowed by its parent constraints.
For example, if you have a Container that's 50x50, and for its child, you pass in another Container that's 300x300, the inner container will be automatically corrected to "not exceed its parent", thus 50x50. Therefore, using sufficiently large values would always make sure you "fill parent".
In fact, even BoxConstraints.expand() exploits the same idea internally. If you open up the source code of expand(), you will see:
/// Creates box constraints that expand to fill another box constraints.
///
/// If width or height is given, the constraints will require exactly the
/// given value in the given dimension.
const BoxConstraints.expand({
double width,
double height,
}) : minWidth = width ?? double.infinity,
maxWidth = width ?? double.infinity,
minHeight = height ?? double.infinity,
maxHeight = height ?? double.infinity;
So if you are absolutely certain you want to fill all spaces, you can intuitively pass in a number bigger than the parent (or larger than the whole screen), like double.infinity.
As of Jan 2020 the simplest is to use an Expanded Widget
Expanded(flex: 1,
child: Container(..),
),
https://api.flutter.dev/flutter/widgets/Expanded-class.html
There are many answers which suggest using two things
constraints: BoxConstraints.expand(),
height: double.infinity,
But both these answer will give you an error like
BoxConstraints forces an infinite height.
We can avoid these by calculating the height of the screen like
App Bar
Top Bar Space(Exist on the above App Bar)
Remaining screen
1. Get the MediaQuery
final mediaQuery = MediaQuery.of(context);
2. Declare the AppBar Widget and same App Bar instance should be used in Scaffold App Bar
final PreferredSizeWidget appBar = AppBar(
title: Text('Home'),
);
3. Use calculated height
Container(
width: mediaQuery.size.width,
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top),
color: Colors.red,
),
Output:
Set the height or width of a container to double.maxFinite
Container(
height: double.maxFinite,
width: 100,)
You can make your widget take the full size of a Container widget, and then set the container's height and/or width to double.maxFinite. This will make the Container take the height and/or width or its parent widget
I propose using Expanded widget (which allows us to avoid IntrinsicHeight widget), combine it with the Container's alignment property and therefore make it work properly even if the Container is not the only one at the screen.
Expanded(
child: Container(
alignment: Alignment.center,
child: Text('Your text', textAlign: TextAlign.center))),
That way one also avoids potential app's crash which occurs often when you accidentally expand to infinity some parts of the widget tree both horizontally and vertically (that is why you are not able to use BoxConstraints widget in many cases).
One can read more about the problems of passing constraints in Flutter here - a must read: https://medium.com/flutter-community/flutter-the-advanced-layout-rule-even-beginners-must-know-edc9516d1a2
This work works for me
height: MediaQuery.of(context).size.height,