Nested container not respecting size constraints - flutter

I have the following code:
#override
Widget build(BuildContext context) => Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
color: Colors.green.withOpacity(0.3),
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.25,
color: Colors.red.withOpacity(0.3),
),
)
I expect to see a red rectangle taking half of the screen, and centered inside a green one taking one quarter of the screen. But instead the green one spans the entire half (same size as the red one).
Why is that happening? How could I get the expected result without using a Stack?
For more context I'm trying to work around the issue with GestureDetector not responding correctly after translation with a Transform widget:
https://github.com/flutter/flutter/issues/27587
EDIT:
After a good night of sleep the solution to achieve the expected result came easily, I added some margin to the inner container as follow:
#override
Widget build(BuildContext context) => Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
color: Colors.green.withOpacity(0.3),
child: Container(
margin: EdgeInsets.symmetric(vertical: MediaQuery.of(context).size.height * 0.25 * 0.5),
color: Colors.red.withOpacity(0.3),
),
);
But I'm way more interested in understanding why the first code is not working as expected. I read the source code for the Container widget but I can't find any obvious cause. Does anyone know?
EDIT 2:
#Pablo Barrera Answers gave me the idea to try adding the alignment to the first container instead (which is the way it should be indeed).
It also makes the second container take the expected size, which is unexpected. Still curious to know if there is a logical explanation for this.
#override
Widget build(BuildContext context) => Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
color: Colors.green.withOpacity(0.3),
child: Container(
height: MediaQuery.of(context).size.height * 0.25,
color: Colors.red.withOpacity(0.3),
),
);

You could use Wrap widget like this:
#override
Widget build(BuildContext context) => Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
color: Colors.green.withOpacity(0.3),
child: Wrap(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.25,
color: Colors.red.withOpacity(0.3),
),
],
)
)
Edit: When adding 'alignment: Alignment.center' to the root Container, behind the scenes it's wrapping it's child with an Align widget, so the Wrap widget wouldn't be necessary anymore because the child Container won't try to expand to fit the root Container

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),
),
),
),
);
}

Fix RenderConstraintsTransformBox overflowed warning

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(),
),
)

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());

If I created a widget, how can I set the width or height as screen ratio?

If I created a widget, how can I set the width or height as screen ratio?
class HomeContent extends StatelessWidget{
#override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network("http://a1.att.hudong.com/60/38/01200000194369136323385641912.jpg"),
width: 500,
height: 500,
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}
You see my upper code, I want the width:Center_height as 1:2, how can I implement?
I mean the Center be carpeted with the screen page, the Container's height : Center's height = 1:2.
instead of directly passing value,
try using MediaQuery class.
Example
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
MediaQuery class will return the height / width of the current device, and you can directly use it accordingly, you can also use it like this,
width: MediaQuery.of(context).size.width / 2,
here, widget will occupy the half width available in current screen.
Use Mediaquery forheight and Width
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
And implement like this
class HomeContent extends StatelessWidget{
#override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network("http://a1.att.hudong.com/60/38/01200000194369136323385641912.jpg"),
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}

Animating Widget to a position outside of it's parent (Row/Listview)

I have a list of card widgets inside a row or listview. When clicking on of these cards i want to create an effect where the card grows and moves to the middle of the screen, and the rest of the screen appears below a grey overlay.
These example images should help you understand what i'm trying to explain.
Image #1 - List before Clicking in any card
Image #2 - After clicking a card. The card animates in size and position to the center of the screen. Everything else gets becomes dark. (Ignore bad Photoshop).
I'm not asking for full code or anything, just want to know if it's possible to move a widget outside it's parent and get some ideas of how to achieve this effect. I know AnimatedContainer can be used on the card to make it grow, the positioning part is what need help with. Thanks!
You can use the transform: argument on a Container()
Full Working Example
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
color: Colors.black87,
margin: EdgeInsets.only(top: 100),
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
transform: Matrix4.translationValues(0, 50, 0),
color: Colors.red,
height: 100,
width: 100,
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
color: Colors.red,
height: 100,
width: 100,
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
color: Colors.red,
height: 100,
width: 100,
),
],
),
)
)
);
}
}
The #2 Animation can be accomplished by using a Hero() Widget and Overlay(). Or you can use a custom DialogBuilder just a few Suggestions to get you started.