Fix RenderConstraintsTransformBox overflowed warning - flutter

I have a widget that exceeds the size of the display and I would like to show different parts depending on user input.
When using this code:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.red,
...
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Transform.translate(
offset: const Offset(-50, 0), // offset hardcoded to -50
child: Container(
width: 2000,
height: 100,
color: Colors.yellow,
),
...
The widget respects the constraints, so the container is fitted to the display. After the transform, you see the background instead of a continuation of the widget.
I could wrap the widget in an UnconstrainedBox:
UnconstrainedBox(
alignment: Alignment.topLeft,
child: Transform.translate(...)
)
This fixes the problem, but results in an error:
A RenderConstraintsTransformBox overflowed by 1500 pixels on the
right.
I want it to overflow, I don't think this is an error! Any ideas on how I can fix this?
Note: I could use a SingleChildScrollView with NeverScrollableScrollPhysics() and use the controller to set position, but to me, this feels like overkill. Hope there is a simpler method. Thanks for the read :)

UnconstrainedBox is not the widget to use for getting rid of inner child overflow warnings. It's used to loosen the constraints.
You can use an OverflowBox in this case. Usage example:
SizedBox(
width: 100,
height: 100,
child: OverflowBox(
minWidth: 150,
minHeight: 150,
maxWidth: 150,
maxHeight: 150,
child: FlutterLogo(),
),
)

Related

Why does a SizedBox in another SizedBox ignore its width and hight?

When I nest two SizedBoxes, the width and height of the inner box are ignored. Why is this, how can I work around it?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 300,
height: 500,
child: SizedBox(
width: 200, height: 200, child: Container(color: Colors.green)),
));
}
}
In this example, I have a 300x500 sized box and an inner 200x200 SizedBox. In the picture you can see that the green box is the size of the outer SizedBox but should actually be a 200x200 square.
According to flutter documentation: If given a child, this widget forces it to have a specific width and/or height. These values will be ignored if this widget's parent does not permit them. For example, this happens if the parent is the screen (forces the child to be the same size as the parent), or another SizedBox (forces its child to have a specific width and/or height). This can be remedied by wrapping the child SizedBox in a widget that does permit it to be any size up to the size of the parent, such as Center or Align.
So wrapping the child with center would solve the problem:
Center(
child: SizedBox(
width: 300,
height: 500,
child: Center(
child: SizedBox(
width: 200, height: 200, child: Container(color: Colors.green)),
),
)),
The problem is, SizedBox can set widget size only within the constrains set by the parent. Many widgets, like Padding, want their child to occupy 100% of the space available to them. This makes sense, because if the child is smaller they wouldn't know where to put it.
If you want the child to be smaller than the parent you could use Center or Align, e.g. replace
I had the same issue and I solved my problem using the FractionallySizedBox class.
You can specify the suitable size using fractions of the above SizedBox as widthFactor and heightFactor:
Widget build(BuildContext context) {
return SizedBox.expand(
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 0.5,
alignment: FractionalOffset.center,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 4),
),
),
),
);
}

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.

Trying to Understand ConstrainedBox

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.

stack positioned fill while rotated

Is there a way to implement a rotated by 90 degrees container that will fill the space available inside a Stack? When I try to set the size of the child of the rotated widget it seems like it is still being limited by the parent widget. I would like to know if there is someway to make it work.
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
// something with size to define the size of the stack
Container(
color: Colors.white,
height: 600,
width: 300,
),
Positioned(
child: LayoutBuilder(builder: (context, constraints) {
return SizedBox(
width: constraints.maxHeight,
height: constraints.maxWidth,
child: Transform.rotate(
angle: math.pi / 2,
// this should have height equal to constraints.maxWidth
// and width equal to constraints.maxHeight
// but the height is equal to constraints.maxWidth
// and the width as well
child: Container(color: Colors.black.withOpacity(0.5)),
),
);
}),
),
],
);
}
You can fix this by using an OverflowBox instead of a SizedBox
See difference between a SizedBox and OverflowBox below:
SizedBox
A box with a specified size.
If given a child, this widget forces its child to have a specific width and/or height (assuming values are permitted by this widget's parent).
OverflowBox
A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.
I hope this helps.
I just figured there is a widget called OverflowBox that can be used to get this behavior. I swapped the SizedBox with a OverflowBox widget and things started to work as expected.
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
// something with size to define the size of the stack
Container(
color: Colors.white,
height: 600,
width: 300,
),
Positioned(
child: LayoutBuilder(builder: (context, constraints) {
return OverflowBox(
maxWidth: constraints.maxHeight,
maxHeight: constraints.maxWidth,
child: Transform.rotate(
angle: math.pi / 2,
// has the expected size
child: Container(color: Colors.black.withOpacity(0.5)),
),
);
}),
),
],
);
}
RotatedBox(
quarterTurns: _rotateAngel,
child: _yourChildWidget());

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,