Luke
Luke

Reputation: 6788

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?

Upvotes: 371

Views: 438099

Answers (20)

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

Touseef khattak
Touseef khattak

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

Suragch
Suragch

Reputation: 512726

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.

FractionallySizedBox
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.

Expanded

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 ...
}

Upvotes: 447

Karma
Karma

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

Bijendra Singh
Bijendra Singh

Reputation: 647

Kindly use a very simple trick

Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, )

Upvotes: 1

TSR
TSR

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

Dheeraj Singh Bhadoria
Dheeraj Singh Bhadoria

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

Anandh Krishnan
Anandh Krishnan

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

enter image description here

Upvotes: 4

Collin Jackson
Collin Jackson

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

Bio BoJlk
Bio BoJlk

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

Kab Agouda
Kab Agouda

Reputation: 7289

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 :

enter image description here

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 :

enter image description here

3- The use of other widgets such as Expanded , Flexible and AspectRatio and more .

Upvotes: 44

user11644496
user11644496

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

Sanjayrajsinh
Sanjayrajsinh

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

enter image description here


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

enter image description here



3. Others Like Flexible and AspectRatio and FractionallySizedBox

Upvotes: 19

Nicolas
Nicolas

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

CopsOnRoad
CopsOnRoad

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

Ian Hickson
Ian Hickson

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

Keshav Aditya R P
Keshav Aditya R P

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

Nitin Mehta
Nitin Mehta

Reputation: 255

MediaQuery.of(context).size.width

Upvotes: 10

parth jansari
parth jansari

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

Rody Davis
Rody Davis

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

Related Questions