Flutter Row widget solve RenderFlex overflow by cutting off remaining area - flutter

There are a lot of questions here already about Renderflex overflow, but I believe my use case might be a bit different.
Just the usual problem - having a Widget that is too big in a Row widget, and I get the A RenderFlex overflowed by X pixels ... error.
I want to create a Row that cuts off it's overflowing child Widget if they would be rendered outside it's area without getting an error.
First off, wrapping the last element in the Row widget with Expanded or Flexible does not work in my case, as recommended here and here and many other places. Please see code and image:
class PlayArea extends StatelessWidget {
#override
Widget build(BuildContext context) {
final dummyChild = Container(
color: Colors.black12,
width: 100,
child: Text('important text'),
);
final fadeContainer = Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black26,
Colors.black87,
],
),
),
width: 600,
);
return Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
color: Colors.redAccent,
child: Column(children: [
Expanded(
child: Row(
children: <Widget>[
dummyChild,
fadeContainer,
],
),
),
Expanded(
child: Row(
children: <Widget>[
dummyChild,
Expanded(
child: fadeContainer,
),
],
),
),
Expanded(
child: Row(
children: <Widget>[
Container(
color: Colors.black12,
width: 1100,
child: Text('important text'),
),
Expanded(
child: fadeContainer,
),
],
),
),
]),
),
);
}
}
Key points:
Using Expanded changes the width of Container, which changes the gradient's slope. I want to keep the gradient as it is
Even with Expanded widget, the Row is not prepared for the case when important text's area is too wide and does not fit the screen horizontally - it will get an overflow error for that Widget
it is technically working in the first case, because no red color is drawn on the right side on the green field, it 'just' has an error
How do I cut off the remaining space dynamically without any error - regardless of any screen size and content?

One solution I found is that
Row(
children: <Widget>[
dummyChild,
fadeContainer,
],
)
can be converted to
ListView(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
dummyChild,
fadeContainer,
],
)
Creating a horizontal list and preventing scroll on that.
edit.: just found out it that you'll get unbounded vertical height, so it's not the same.

Related

Fixed sized button and expanded textfield inside Row Flutter

I want to develop a UI just like Postman Desktop App. But When I add a text field and fix the sized button in a row it gives an error RenderFlex children have non-zero flex but incoming width constraints are unbounded. But I don’t want to give a fixed size to the text field through MediaQuery. As you can see in this video (Demo UI) Postman text field size is fixed when the screen size decrease it shows a horizontal scrollbar. I want the same behavior. Here is the code.
body: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
width: 300,
decoration:
BoxDecoration(border: Border.all(color: Colors.yellow)),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red)),
child: Image.network(
ImageURL,
fit: BoxFit.fitWidth,
),
),
],
),
),
),
SizedBox(
child: Column(
children: [
Row(
children: <Widget>[
const Expanded(child: TextField()),
DarkButton("Save", () {}),
],
),
],
),
),
],
),
),
),
);
Add isDens = true in Textfield.
Just like this:
Expanded(child: TextField(decoration: InputDecoration(isDense: true),)),
this is because you use Expanded inside the SingleChildScrollView.
You can not use Expanded inside the SingleChildScrollView because it covers the maximum width or height.
Just remove SingleChildScrollView error was gone.
you may use listview for this or use CustomScrollView for that

Expand Container to fill remaining space in Column

I have a problem with building a specific layout in flutter.
What I need is:
Scrollable screen, with two columns, where the first col is of certian (but dynamic) height. And the second col has part of certain (but dynamic) height and the rest of the col I want to fill remaining space.
The code structure.
Row(
children: [
Expanded(
child: Column(children:[
someDynamicHeightWidgetsHere()])),
Expanded(child: Column(children: [
someWidgetsHere(),
here fill remainind space to match the first column]))
]
)
Use IntrinsicHeight, This widget only expands to its child height.
This class is useful, for example, when unlimited height is available and
you would like a child that would otherwise attempt to expand infinitely to
instead size itself to a more reasonable height.
Example:
IntrinsicHeight(
child: Row(
children: [
Expanded(
child: Column(
children: [
Container(
height: 800,
color: Colors.blue,
),
],
),
),
Expanded(
child: Column(
children: [
Container(
height: 400,
color: Colors.red,
),
Expanded(
child: Container(
color: Colors.yellow,
),
),
],
),
),
],
),
Output:

Expanded and flexible not filling entire row

Hello I have the below code
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
// FlexibleWidget(),
ExpandedWidget(),
FlexibleWidget(),
],
),
Row(
children: [
ExpandedWidget(),
ExpandedWidget(),
],
),
Row(mainAxisSize: MainAxisSize.min,
children: [
FlexibleWidget(),
FlexibleWidget(),
],
),
Row(mainAxisSize: MainAxisSize.min,
children: [
FlexibleWidget(),
ExpandedWidget(),
],
),
],
),
);
}
}
class ExpandedWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.green,
border: Border.all(color: Colors.white),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Expanded',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
);
}
}
class FlexibleWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Flexible(
child: Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Flexible',
style: TextStyle(color: Colors.black, fontSize: 24),
),
),
),
);
}
}
And the result is this:
Shoudln't the first container fill the space left after the flexible widget is set? Since it also belongs to a column with a child of 2 expanded widgets, the column width size should be the entire screen. That means the row should be the entire screen too. I would have expected the expanded green widget in the first row to fill the row until there is no white left like the image below:
Instead it only fills half the page. Can anyone explain this behaviour?
Let me start with FlexFit before coming to the point. FlexFit.tight allows the children to fill rest of the space but FlexFit.loose allows children to size themselves as they want.
Now, as you might know Expanded is nothing but Flexible with tight-fitting i.e. fit: FlexFit.tight.
Expanded(
child: child,
)
is equivalent to
Flexible(
fit: FlexFit.tight,
child: child,
)
Now, Let's take your example.
For the first block, there are two widgets with Flex 1 (by default), which will divide the screen to two half; each will be given 50%. This does not mean that they have to take 50% of the screen. It means that they can expand to 50% of the screen. So, the Expanded widget does that. It takes 50% of the screen as it has tight-fitting.
Coming to the Flexible widget, by default, it follows loose-fitting. So it says to its children that they can expand till 50% but there is no boundation that they have to take all space. They can take whatever space they want but the upper bound is set to 50%. In your case, it only needs the space to fit "Flexible" text with some padding. So it takes that.
Pushpendra explained how flex works and why they take at most half the screen when using two flex items. However, there is no answer showing how to make one item fill the rest of the space including space that is not taken by the other item.
The trick is to only use a single flex item that takes all space (Expanded) and constraining the other item to maxWidth = half of the available width. This can be achived using LayoutBuilder to get the available width and ConstraintedBox to constrain one widget without using flex - which allows the Expanded to take more than half of the space (all that is not taken by the ConstrainedBox):
LayoutBuilder(builder: (context, constraints) {
return Row(
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: constraints.maxWidth * 0.5),
child: Text(
text1,
overflow: TextOverflow.fade,
softWrap: false,
),
),
Expanded(
child: Text(
text2,
overflow: TextOverflow.fade,
softWrap: false,
),
),
],
);
});

Flutter, How shadow clipping system work?

I'm so confused about how the clipping system in Flutter works.
So I will start with this example, Non of the shadow has been clipped.
Even the shadow was overflow from its self or its parent container.
Container(
child: Column(
children: [
Text("test"),
Container(
margin: paddingTheme.edgeInsets,
child: Row(
children: [
ShadowBox(),
ShadowBox(),
ShadowBox(),
],
),
),
],
),
)
Now if I add more ShadowBox() until it overflows, All the shadow will be clipped.
For example:
Container(
child: Column(
children: [
Text("test"),
Container(
margin: paddingTheme.edgeInsets,
child: Row(
children: [
ShadowBox(),
ShadowBox(),
ShadowBox(),
ShadowBox(), // ADD THIS
],
),
),
],
),
)
Now even I change Row to be SingleChildScrollView or List it's still clipped, But not overflow
Container(
child: Column(
children: [
Text("test"),
Container(
margin: paddingTheme.edgeInsets,
child: ListView.builder( // CHANGE FROM ROW TO LIST
scrollDirection: Axis.horizontal,
itemCount: 5,
itemBuilder: (c, i) => ShadowBox(),
),
),
],
),
)
So the question is how it's work, And how can I prevent these shadows to be clipped?
Or it's a design flaw of Flutter itself?
I had fire this in the Flutter's issue.
https://github.com/flutter/flutter/issues/67858
As I understand from now, The default ClipBehaviour of Container Row Column is different from ListView and internal Overflow.
So we have a workaround by adding clipBehaviour: Clip.none to the ListView.
Edit: To get the overlap shadows and no clipping, you can try:
In ShadowBox() widget,
Container(
height: 100,
width: 100,
margin: EdgeInsets.only(top: 20,bottom: 20), <-----
decoration:BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.blue,blurRadius: 12)]
),
),
As for why it is clipped?
In Flutter engine, clipping the shadow is default behavior.
If you go deep in code you will find that shadow's width/height is not taken into account while rendering children in ListView or any widget. So you have to take care of it.
Why is it not clipping in Row/Column then?
In Rows and columns, no clipping is visible because it assumes the height of Row/Column as per the parent's max height/width. So it's height/width is at Max/Infinity.
So if you increase the width and height of the ListView()'s parent Container() widget, clipping will not happen as well.

Expand widgets inside the Stack widget

I have a stack widgets with two widgets inside.
One of them draws the background, the other one draws on top of that.
I want both widgets to have the same size.
I want the top widget, which is a column, to expand and fill the stack vertically to the size that is set by the background widget.
I tried setting the MainAxis mainAxisSize: MainAxisSize.max but it didn't work.
How can I make it work?
Use Positioned.fill
Stack(
children: [
Positioned.fill(
child: Column(
children: [
//...
],
),
),
//...
],
);
More info about Positioned in Flutter Widget of the Week
How does it work?
This is all about constraints. Constraints are min/max width/height values that are passed from the parent to its children. In the case of Stack. The constraints it passes to its children state that they can size themselves as small as they want, which in the case of some widgets means they will be their "natural" size. After being sized, Stack will place its children in the top left corner.
Positioned.fill gets the same constraints from Stack but passes different constraints to its child, stating the it (the child) must be of exact size (which meant to fill the Stack).
Positioned.fill() is the same as:
Positioned(
top: 0,
right: 0,
left: 0,
bottom:0,
child: //...
)
For even more examples and info: How to fill a Stack widget and why? and Understanding constraints.
You can try wrapping the Column with a positioned widget. Set top to 0 and bottom to 0. This will make the column fill the stack vertically.
sample code:
Stack(
children: <Widget>[
// Background widget rep
Container(
color: Colors.pink,
),
Positioned(
top: 0,
bottom: 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('madonna'),
Text('flutter'),
],
),
)
],
)
I think you could wrap you column in a container like this:
Container(
height: double.infinity,
width: double.infinity,
child: Column()
),
The accepted answer did not answer my issue completely. So, I put my code here in case you have a similar issue.
class NoContentView extends StatelessWidget {
const NoContentView({Key? key, required this.message}) : super(key: key);
final String message;
#override
Widget build(BuildContext context) {
return Expanded(
child: Stack(
children: [
Positioned.fill(
child: FittedBox(
child: const Image(image: AssetImage(AppImages.noReceiptsWeb), fit: BoxFit.fitWidth),
),
),
MessageView(message: message),
],
),
);
}
}
In my case, I thought that I needed to expand the container in the light blue color with an expanded widget inside the stack widget.
Problems arose, especially since I was adding the stack widget in the column widget, and the way to make the container double.infinity dimensions I definitely had a problem with the vertical axis.
In the end, I managed to show it like this photo...
Column(
children: [
//filter
Container(
color: orange,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Text(
'All',
style: getRegular(color: white.withOpacity(.6)),
),
),
Container(
color: white.withOpacity(.6),
width: 1,
height: 50,
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 10, 0),
child: Text(
'Ready',
style: getRegular(color: white.withOpacity(.6)),
),
),
freeh(),
Container(
color: white.withOpacity(.6),
width: 1,
height: 50,
),
],
),
Row(
children: [
Icon(
filter,
color: white.withOpacity(.7),
size: 20,
),
Text(
'FILTER',
style: getLight(
color: white.withOpacity(.7),
),
),
freeh(),
],
),
],
),
),
Expanded(
child: Stack(
alignment: Alignment.topLeft,
children: [
Container(
color: lightBlue, //here to use map>>>>
),
Container(
height: 35,
width: 120,
margin: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: primary,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
FontAwesomeIcons.listUl,
color: white.withOpacity(.8),
size: 20,
),
Text(
'List View',
style: getRegular(color: white.withOpacity(.8)),
)
],
),
),
],
),
)
],
),