Sizing elements to percentage of screen width/height
Asked Answered
G

18

360

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?

Galluses answered 30/3, 2017 at 15:26 Comment(2)
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).Boer
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.Tagore
O
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.

Odelsting answered 31/3, 2017 at 0:45 Comment(3)
What if you need to get the width of the immediate parent widget?Difficult
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.Fragmental
if i change the display size in from the phone setting, then the ratio set (0.65 in your example) might not be suitable...Myriapod
B
431

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 ...
}
Borosilicate answered 7/12, 2018 at 5:8 Comment(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.Aciculate
@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 elseDiazole
@Shiva, I think it is the available space, not the total screen size. The available space is determined by the parent widget.Borosilicate
O
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.

Odelsting answered 31/3, 2017 at 0:45 Comment(3)
What if you need to get the width of the immediate parent widget?Difficult
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.Fragmental
if i change the display size in from the phone setting, then the ratio set (0.65 in your example) might not be suitable...Myriapod
U
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.

Unswerving answered 14/3, 2018 at 17:5 Comment(0)
E
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 .

Erinn answered 10/12, 2020 at 14:29 Comment(0)
O
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.

Osmosis answered 30/3, 2017 at 22:58 Comment(0)
R
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

Retrieve answered 3/12, 2019 at 7:31 Comment(0)
P
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
Parthena answered 7/7, 2019 at 15:27 Comment(1)
This saved my life. Though, I must point out that the context (used by abovePadding) has to be the one from the appBar.Lallygag
H
10
MediaQuery.of(context).size.width
Hex answered 2/6, 2018 at 21:7 Comment(0)
G
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

Gustavo answered 28/11, 2018 at 3:58 Comment(0)
F
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,
        ...
Foote answered 8/1, 2020 at 16:12 Comment(0)
K
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

Kienan answered 22/7, 2022 at 13:15 Comment(0)
B
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
Bab answered 28/3, 2018 at 16:0 Comment(0)
C
2

Code :

MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
Corticosterone answered 30/11, 2020 at 17:58 Comment(0)
C
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,
            ),
Caliche answered 6/9, 2021 at 15:0 Comment(0)
I
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

Inkstand answered 27/12, 2022 at 9:37 Comment(0)
P
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.

Putnem answered 13/1, 2023 at 4:37 Comment(0)
S
1

Kindly use a very simple trick

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

Somerset answered 29/5, 2023 at 4:33 Comment(1)
Simple Hack. UsefullJerz
P
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()
  );
}
Planchette answered 22/9, 2023 at 18:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.