359

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?

2
  • 1
    Just saying, this is generally not good practice IMO, that's web design stuff. In apps you should generally use device pixels to define sizes, and use Paddings to inset your widgets, since your aspect ratio is almost always the same (except for tablets).
    – leodriesch
    Feb 18, 2019 at 20:18
  • 36
    After another 1.5 years, it seems like this isn't such a bad idea after all. Every month there's new phones coming out with weirder and weirder aspect ratio.
    – Farid
    Oct 21, 2019 at 8:03

18 Answers 18

430

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 ...
}
3
  • Note that if you're building anything remotely similar to a progress bar, you'll likely want to set FractionallySizedBox constructor alignment parameter to Alignment.centerLeft, as the default is Alignment.center.
    – cubuspl42
    Sep 16, 2021 at 10:15
  • @Suragch, May be a dumb question, when we use FractionallySizedBox, what is the reference used to calculate height or width factor, is it total screen size or something else
    – Shiva
    Oct 27, 2022 at 9:24
  • @Shiva, I think it is the available space, not the total screen size. The available space is determined by the parent widget.
    – Suragch
    Oct 31, 2022 at 8:28
363

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.

3
  • What if you need to get the width of the immediate parent widget?
    – Harsha
    Nov 21, 2022 at 6:23
  • 1
    This returns the size of the window rather than screen, ie on a system that supports windows this value will change as you resize the window rather than being a fixed value based on the screen's pixel count. Jan 21, 2023 at 20:11
  • if i change the display size in from the phone setting, then the ratio set (0.65 in your example) might not be suitable...
    – Leong
    Jan 30 at 10:24
154

This might be a little more clear:

double width = MediaQuery.of(context).size.width;

double yourWidth = width * 0.65;

Hope this solved your problem.

43

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 .

24

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.

19

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

0
16

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
1
  • 2
    This saved my life. Though, I must point out that the context (used by abovePadding) has to be the one from the appBar.
    – Doctor
    Sep 27, 2020 at 21:30
10
MediaQuery.of(context).size.width
0
7

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

7

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,
        ...
3

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

2

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
2

Code :

MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
2

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

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

1

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.

0
1

Kindly use a very simple trick

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

1
0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.