stack positioned fill while rotated - flutter

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

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

Why parent Container constraints are respected by child Container when OverflowBox is provided?

Minimal code:
Widget build(_) {
return Scaffold(
body: Container(
width: 100, // 'A' width
height: 30,
child: OverflowBox(
maxWidth: 400, // 'B' width
child: Container(
width: 200, // 'C' width
color: Colors.white,
),
),
),
);
}
If you chang A width to 0, you'll find the the width (C) of white Container decreases which shouldn't happen because I'm providing OverflowBox which seems to provide its own constraints to its child to let it overflow.
In my understanding, the constraints passed by 1st Container shouldn't have any impact on 2nd Container as long as OverflowBox provides its own max/min to it.
Output:

Nested container not respecting size constraints

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

Sizing elements to percentage of screen width/height

Is there a simple (non-LayoutBuilder) way to size an element relative to screen size (width/height)? For example: how do I set the width of a CardView to be 65% of the screen width.
It can't be done inside the build method (obviously) so it would have to be deferred until post build. Is there a preferred place to put logic like this?
This is a supplemental answer showing the implementation of a couple of the solutions mentioned.
FractionallySizedBox
If you have a single widget you can use a FractionallySizedBox widget to specify a percentage of the available space to fill. Here the green Container is set to fill 70% of the available width and 30% of the available height.
Widget myWidget() {
return FractionallySizedBox(
widthFactor: 0.7,
heightFactor: 0.3,
child: Container(
color: Colors.green,
),
);
}
Expanded
The Expanded widget allows a widget to fill the available space, horizontally if it is in a row, or vertically if it is in a column. You can use the flex property with multiple widgets to give them weights. Here the green Container takes 70% of the width and the yellow Container takes 30% of the width.
If you want to do it vertically, then just replace Row with Column.
Widget myWidget() {
return Row(
children: <Widget>[
Expanded(
flex: 7,
child: Container(
color: Colors.green,
),
),
Expanded(
flex: 3,
child: Container(
color: Colors.yellow,
),
),
],
);
}
Supplemental code
Here is the main.dart code for your reference.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("FractionallySizedBox"),
),
body: myWidget(),
),
);
}
}
// replace with example code above
Widget myWidget() {
return ...
}
FractionallySizedBox may also be useful.
You can also read the screen width directly out of MediaQuery.of(context).size and create a sized box based on that
MediaQuery.of(context).size.width * 0.65
if you really want to size as a fraction of the screen regardless of what the layout is.
This might be a little more clear:
double width = MediaQuery.of(context).size.width;
double yourWidth = width * 0.65;
Hope this solved your problem.
There are several possibilities:
1- The first one is the use of the MediaQuery :
Code :
MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
Example of use :
Container(
color: Colors.yellow,
height: MediaQuery.of(context).size.height * 0.65,
width: MediaQuery.of(context).size.width,
)
Output :
2- The use of FractionallySizedBox
Creates a widget that sizes its child to a fraction of the total available space.
Example :
FractionallySizedBox(
widthFactor: 0.65, // between 0 and 1
heightFactor: 1.0,
child:Container(color: Colors.red
,),
)
Output :
3- The use of other widgets such as Expanded , Flexible and AspectRatio and more .
You could build a Column/Row with Flexible or Expanded children that have flex values that add up to the percentages you want.
You may also find the AspectRatio widget useful.
There is many way to do this.
1. Using MediaQuery : Its return fullscreen of your device including appbar,toolbar
Container(
width: MediaQuery.of(context).size.width * 0.50,
height: MediaQuery.of(context).size.height*0.50,
color: Colors.blueAccent[400],
)
2. Using Expanded : You can set width/height in ratio
Container(
height: MediaQuery.of(context).size.height * 0.50,
child: Row(
children: <Widget>[
Expanded(
flex: 70,
child: Container(
color: Colors.lightBlue[400],
),
),
Expanded(
flex: 30,
child: Container(
color: Colors.deepPurple[800],
),
)
],
),
)
3. Others Like Flexible and AspectRatio and FractionallySizedBox
First get the size of screen.
Size size = MediaQuery.of(context).size;
After this you can get width and multiply it with 0.5 to get 50% of screen width.
double width50 = size.width * 0.5;
But problem generally comes in height, by default when we use
double screenHeight = size.height;
The height we get is global height which includes StatusBar + notch + AppBar height. So, in order to get the left height of the device, we need to subtract padding height (StatusBar + notch) and AppBar height from total height. Here is how we do it.
double abovePadding = MediaQuery.of(context).padding.top;
double appBarHeight = appBar.preferredSize.height;
double leftHeight = screenHeight - abovePadding - appBarHeight;
Now we can use following to get 50% of our screen in height.
double height50 = leftHeight * 0.5
MediaQuery.of(context).size.width
you can use MediaQuery with the current context of your widget and get width or height like this
double width = MediaQuery.of(context).size.width
double height = MediaQuery.of(context).size.height
after that, you can multiply it with the percentage you want
Use the LayoutBuilder Widget that will give you constraints that you can use to obtain the height that excludes the AppBar and the padding. Then use a SizedBox and provide the width and height using the constraints from the LayoutBuilder
return LayoutBuilder(builder: (context2, constraints) {
return Column(
children: <Widget>[
SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
...
if you are using GridView you can use something like Ian Hickson's solution.
crossAxisCount: MediaQuery.of(context).size.width <= 400.0 ? 3 : MediaQuery.of(context).size.width >= 1000.0 ? 5 : 4
Code :
MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
You can use the Align widget. The heightFactor and widthFactor parameters are multiplied by the size of the child widget. Here is an example that will make a widget with a fixed height in% ratio
Align(
alignment: Alignment.topCenter,
heightFactor: 0.63,
widthFactor: ,
child: Container(
width: double.infinity,
),
Use scaler to define the layout width and height in percentage
dependencies:
scaler: ^1.1.0+1
After setting this in pubspec.yaml you can use this by following the code -
import 'package:scaler/scaler.dart';
Example After import use this -
import 'package:scaler/scaler.dart';
#override
Widget build(BuildContext context) {
/**
* Container with 25% width of screen
* and 25% height of screen
*/
return Container(
height: Scaler.height(0.25, context),
width: Scaler.width(0.25, context),
child: Container()
);
}
To more detail about this
https://pub.dev/packages/scaler
For width
double width = MediaQuery.of(context).size.width;
double yourWidth = width * 0.75;
For Height
double height = MediaQuery.of(context).size.height;
double yourHeight = height * 0.75;
If you don't want static height and width just use Expanded widget\
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatelessWidget(),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Expanded Row Sample'),
),
body: Center(
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.amber,
height: 100,
),
),
Container(
color: Colors.blue,
height: 100,
width: 50,
),
Expanded(
child: Container(
color: Colors.amber,
height: 100,
),
),
],
),
),
);
}
}
I am surprised that no one has yet suggested LayoutBuilder in 2023 which gives you access to the parent's width BoxConstraints.constraints.maxWidth, which is the most versatile method.
Expanded can only set the percentage of the available spacing, but what if you really want to set a percentage based on the actual parent's widget only, not the entire screen width, what if have a fixed width widget in a row, even a more complex, what if you also want an Expanded to expand the remaining Row.
MediaQuery.of(context).size.width is relative to the entire screen, not to the actual parent.
FractionallySizedBox works similarly but you can't put it in Row
Also, this method the perfect emulation of CSS % unit of measurement.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, BoxConstraints constraints) {
return SizedBox(
width: 470,
child: Row(
children: [
SizedBox(
width: 200,
child: Icon(
Icons.link,
color: Colors.white,
),
),
SizedBox(
width: constraints.maxWidth*0.6,
child: Icon(
Icons.message,
color: Colors.white,
),
),
Expanded(
child: Icon(
Icons.phone,
color: Colors.white,
),
),
SizedBox(
width: constraints.maxWidth*0.3,
child: Icon(
Icons.account_balance,
color: Colors.white,
),
),
],
),
);
}
);
}
}
In this example, we are setting a parent widget of 470 width with Row inside. In the Row, one element has a 200 fixed width, another with a 60% of the parent with 470, another with 30% of that same parent, and another expanding any remaining space.