how to give a widget, a specific width according to its parent - Flutter - flutter

there is a scenario that i have a circle and inside that circle i want to show some text. Both items are in a scrollable listview. i want to calculate the width of the parent and then give with to the inner children to be placed and shouldn't move backward or forward from its outer parent circle widget.
i have used stack widget to do so which works fine on a single device or screen but what about other screen sizes?
any suggestion according to this?

I asked yesterday the same concept question:
the FractionallySizedBox is super useful on those cases, it gives the width / height of a children based on the width / height of the parent:
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ParentCircle(
height: 250,
width: 250,
color: Colors.red,
child: FractionallySizedBox(
widthFactor: .5,
heightFactor: .5,
alignment: FractionalOffset.center,
child: ChildCircle(),
),
),
),
),
);
}
}
this will give the ChildCircle a 50% width and 50% height of the ParentCircle

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

flutter icon as background with overflow

I'm trying to recreate the weather app design from this link UI design. However i'm stuck at creating the huge Icon that is overflowing in the background. I tried to use a stack and add an Icon widget with very big size. However, the icon will change its position when the size is changed. So it moves from the center. Any idea on how to get the background result as in the design?
Edit:
I tried the code below. However the icon will move if the screen size of the device changes. Is there away to have this look the same on every screen size?
Stack(
children: [
Positioned(
left: -250,
top: -100,
child: Icon(
Icons.ac_unit,
size: 900,
color: Colors.white.withOpacity(0.5),
),
),
Which gives me the result below.
Use the widget: OverflowBox
The Flutter documentation explains OverflowBox as:
A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.
Example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Overflow Box',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
backgroundColor: Colors.blue,
body: Stack(children: [
OverflowBox(
// Specify the maxWidth and maxHeight and it works
// regardless of orientation.
maxWidth: 800,
maxHeight: 800,
child: Icon(
Icons.ac_unit,
size: 800,
color: Colors.black.withAlpha(80),
),
),
]),
),
);
}
}
Image of the code in action:
Using Stack is fine. Did you use Positioned or Align to wrap the Icon widget, to control its position?
For example, your widget hierarchy could be something like: Stack > Align > Icon.
In the Align widget, you can specify how it aligns in the Stack. The values are (x, y) ranging from -1 to +1, with 0 being the center. For example, you can use alignment: Alignment(0, -0.5) to make it center horizontally, and 50% towards the top on the vertical axis.
if you will use Positioned widget, you can actually positioned based on screen height and width and then warp in FractionalTranslation to center it from its given size,
lets calculate with simple way :
code :
return Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width * 0.35,
top: MediaQuery.of(context).size.height * 0.4,
child: FractionalTranslation(
translation: const Offset(-0.5, -0.5),
child: child()),
)
],
);
I like the answer that #Tor-Martin Holen suggested, but I would adjust the maxWidth and size attributes to MediaQuery.of(context).size.width to adjust to different screens.

Handling stack when the size of the children exceeds screen size

It is very easy to control the layout using Stack when the children's size is less than that of the screen. But when the children's height is greater than that of the screen size, you will need to use SingleChildScrollView. But if I use this, the whole screen goes white and produces the error shown below.
How can I handle the layout when using Stack & SingleChildScrollView in these circumstances.
Any help is greatly appreciated. Thank you.
Sample code
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: SingleChildScrollView(
child: Stack(
fit: StackFit.expand,
children: [
_Background(),
Positioned(left: 0, top: 100, child: WidgetA()),
Positioned(left: 0, top: 270, child: WidgetB()),
],
),
),
),
),
);
}
}
class _Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
);
}
}
class WidgetA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: randomHeight(),
width: MediaQuery.of(context).size.width,
color: Colors.purple,
child: Center(
child: Text('Widget A'),
),
);
}
}
class WidgetB extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 250,
width: MediaQuery.of(context).size.width,
color: Colors.yellow,
child: Center(
child: Text('Widget B'),
),
);
}
}
Error
════════ Exception caught by rendering library ═════════════════════════════════
RenderBox was not laid out: RenderRepaintBoundary#2e73f relayoutBoundary=up1 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1940 pos 12: 'hasSize'
The relevant error-causing widget was
Scaffold
lib/main.dart:11
════════════════════════════════════════════════════════════════════════════════
Cause:
The SingleChildScrollView Provides Infinite Space.
The stack sizes itself to contain all the non-positioned children, which are positioned according to alignment (which defaults to the top-left corner in left-to-right environments and the top-right corner in right-to-left environments). The positioned children are then placed relative to the stack according to their top, right, bottom, and left properties.
The widget inflates forever.
It will also cause problems with the StackRender Algorithm as that will deal with the Zenith.
Read more in the api documentation here.
Solution give it a Constraint such as a SizedBox();
Wrap your stack with a container and define it's height and width with MediaQuery, then wrap the container with a column.
first the whole screen goes white because the StackFit.expand tell the stack to expand as much as possible and since it is inside SingleChildScrollView that is infinity and can't be handled.
second is that the stack height is defined based on its children and the Position widget height is not calculated , like css position:absolute; if you are familiar with that , so the remaining is your _Background whcih is the widget that define the height of the stack ..
so now SingleChildScrollView and its child has (screen height) height = no scrolling and to solve this we have to specify height enough for our positioned widgets to appear in and for our stack to be able to scroll in.
How to fix
first in your stack change fit to
child: Stack(
fit: StackFit.loose,
second your background which is responsible for the height as mentioned above
class _Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: MediaQuery.of(context).size.height + 250, // <-- change this
width: MediaQuery.of(context).size.width,
);
}
}

BoxConstraints forces an infinite height

child:Column(
children: <Widget>[
Container(
height: double.infinity,
width: 100.0,
color: Colors.red,
child: Text('hello'),
),)
in this,when i make height:double.infinity,it gives error in run saying **BoxConstraints forces an infinite height.**but when i give height manually it work fine.
can anyone explain me why this happening.
How about this one.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
Expanded(
child: Container(
// height: double.infinity,
width: 100.0,
color: Colors.red,
child: Text('hello'),
),
),
],
),
),
);
}
}
This means that you can't offer inifite height to the container. It's obvious behaviour if you don't provide the contraints to height.
You have to specify limited height to the container so that flutter can render it, if you offer it infinite it how can flutter render that and up to which constraints it would do that !
Rather you can set double.infinity to width and flutter will successfully render that because by default flutter has constraints for width it will set width to width of screen.
Considering that you have to provide height as that of screen you can use MediaQuery for that
Widget yourMethod(or build)(BuildContext context){
final screenHeight = MediaQuery.of(context).size.height;
return Column(
children:<Widget>[
Container(
height:screenHeight,//but this will be height of whole screen. You need to substract screen default paddings and height of appbar if you have one
width:100.0,
....
)
]);
}
Hope this helps !
Happy coding..
BoxConstraints forces an infinite height
Why This Happens
You're asking to render an infinite height object without a height constraint... Flutter can't do that.
Column lays out children in two phases:
Phase 1: non-Flex items (anything not Expanded, Flexible or Spacer)
done in unconstrained space
Phase 2: Flex items (Expanded,Flexible, Spacer only)
done with remaining space
Phase 1
Column's phase 1 vertical layout is done in unbounded space. That means:
no vertical constraint → no height limit
any widget with infinite height will throw the above error
you can't render an infinite height object in an infinite height constraint... that's goes on forever
Phase 2
after Phase 1 widgets have taken as much space as they intrinsically need, phase 2 Flex items share the remaining/leftover space
the remaining space is calculated from incoming constraints minus Phase 1 widgets dimensions
double.infinity height will expand to use up the remaining space
Infinite Height is OK
Here's an example of using infinite height on a Container inside a Column, which is fine:
class ColumnInfiniteChildPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Flexible(
child: Container(
height: double.infinity, // ← perfectly fine
child: Text('Column > Container > Text')),
),
Text('Column > Text')
],
),
),
);
}
}
Remove the Flexible and the error will be thrown.

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.