Reputation: 6788
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?
Upvotes: 371
Views: 438099
Reputation: 1151
As announced at Google I/O 2024, with the launch of Flutter 3.22, it is recommended to use:
MediaQuery.sizeOf(context).width;
MediaQuery.sizeOf(context).height;
Instead of:
MediaQuery.of(context).size.width;
MediaQuery.of(context).size.height;
sizeOf(context)
rebuilds any time that property size is changed, while of(context.size)
rebuilds the widgets whenever there is a change in MediaQuery, making it slower to render the view.
Reference: https://www.youtube.com/watch?v=LeKLGzpsz9I&t=13m30s
Upvotes: 3
Reputation: 255
The more clear answer is:
Declare the variable under Widget build(){} body
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
double screenWidth = size.width;
double screenheight = size.width;
example of use:
Container(
color: Colors.blue,
height: screenheight * 0.65,
width: screenWidth * 0.5,
)
Upvotes: 4
Reputation: 512726
This is a supplemental answer showing the implementation of a couple of the solutions mentioned.
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,
),
);
}
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,
),
),
],
);
}
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 ...
}
Upvotes: 447
Reputation: 601
Run this in the terminal
$ flutter pub add scaler
import this:
import 'package:scaler/scaler.dart';
then, this is an example:
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()
);
}
Upvotes: 0
Reputation: 647
Kindly use a very simple trick
Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, )
Upvotes: 1
Reputation: 20627
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.
Upvotes: 2
Reputation: 377
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
Upvotes: 2
Reputation: 6022
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,
),
),
],
),
),
);
}
}
Upvotes: 4
Reputation: 116838
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.
Upvotes: 25
Reputation: 31
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,
),
Upvotes: 2
Reputation: 7289
There are several possibilities:
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 :
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 :
Expanded
, Flexible
and AspectRatio
and more .Upvotes: 44
Reputation:
Code :
MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
Upvotes: 2
Reputation: 17249
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
Upvotes: 19
Reputation: 174
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,
...
Upvotes: 7
Reputation: 268474
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
Upvotes: 17
Reputation: 8609
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.
Upvotes: 375
Reputation: 3221
This might be a little more clear:
double width = MediaQuery.of(context).size.width;
double yourWidth = width * 0.65;
Hope this solved your problem.
Upvotes: 154
Reputation: 176
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
Upvotes: 8
Reputation: 1995
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
Upvotes: 2