I have a list of widgets (from a list of objects that have an x and y coordinate value) of the same size I want display in a grid arrangement, the size and placement wont change.
My initial thought is to use 2 for loops for the columns and rows and pass the widgets as children accordingly.
But would a Stack widget or CustomMultiChildLayout be more performant and suited for this? Given that columns and rows are made to be more dynamically sized and I don't need that functionality.
Edit: Image, literally just a grid of tiles at the moment. It's currently using loops of columns+rows. Yes I will be flipping the Y around to start at the bottom corner.
You can use a gridview to create this layout
class MyHomePage extends StatelessWidget{
#override
Widget build(BuildContext context){
return Scaffold(
body: GridView.count(
crossAxisCount: 15,
children: List.generate(225, (index) {
return GridTile(
child: Container(
height: 50,
width: 50,
color: Colors.blue.shade200,
child: Center(
child: Text('$index'),
)
),
);
}),
),
);
}
}
Or In a wrap
Wrap(
children: List.generate(225, (index) => Container( width: 20, height: 20, child: Text("$index")))
)
As a demo, I have taken two container inside Column and I have used MediaQuery height for both container and deducting size of appear..eventhought it is showing 24 pixel overflow...and if I wrap column with SingleChildScrollView..it scrolls which should be not scrolled as both container's height sum is 1.
here is my demo code
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appbar=AppBar();
return Scaffold(
appBar: appbar,
body: SingleChildScrollView(
child: Column(
children: [
Container(
height: (MediaQuery.of(context).size.height-appbar.preferredSize.height)*0.40 ,
color: Colors.green,
),
Container(
height: (MediaQuery.of(context).size.height-appbar.preferredSize.height)*0.60 ,
color: Colors.red,
),
],
),
)
);
}
}
Column has property 'mainAxisSize', setting that to MainAxisSize.min will solve the problem.
Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: (MediaQuery.of(context).size.height-appbar.preferredSize.height)*0.40 ,
color: Colors.green,
),
In this case Column just stretches to infinity as ScrollView above allows it.
I got my answer from stack overflows history ....
here what I missed to deduct status bar height
height: (MediaQuery.of(context).size.height-appbar.preferredSize.height-MediaQuery.of(context).viewPadding.top)*0.60 ,
I want to make my screen into 3 equal part using container class in flutter. How to do that?
What you want is the Flexible widget
Let's say you want the three equal parts to be stacked vertically
return Column(
children: [
Flexible(child: Container()),
Flexible(child: Container()),
Flexible(child: Container()),
]
);
You can also specify a flex component to signify the relationship between the widgets' sizes
return Column(
children: [
Flexible(child: Container(), flex:1),
Flexible(child: Container(), flex:2),
]
);
which will make the second widget take 2/3 of the available space
I prefer using LayoutBuilder, it constraints that can be used to get body height,maxWidth, also it provides more than this. Also, you can try media query.
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => Column(
children: [
Container(
height: constraints.maxHeight / 3,
width: constraints.maxWidth,
color: Colors.deepPurple,
),
Container(
height:size.height/3,
width: size.width/3,
color: Colors.deepPurple,
)
],
),
));
}
You can remove Column it's just an example. Using LayoutBuilder provide parents' constraints.
I have a widget that needs to draw on the remaining height where that widget needs to be drawn in between the top and bottom widget(Bottom widget drawn with bottom center attribute).
Ex:
Container() -- The First container almost take half screen
Container() -- The Second container draw on the bottom of the screen eg: Consider widget as Button
Container() -- The Third Container needs to be drawn in between two containers.
The Expanded widget would work for this scenario where we need to wrap middle widget with it.
Eg:
Expanded(
child: Container(
...
),
),
I know a better method using MediaQuery.of(), where you just put the percentage of the space available that a widget will cover:
final mediaQuery = MediaQuery.of(context);
const SizedBox(height: mediaQuery.size.heigh * [percentage of the screen]
But this way includes AppBar space too. So if you want to exclude it, you should do this:
Create appBar variable:
final PreferredSizeWidget appBar = AppBar(
title: const Text('Personal expensense'),
);
Subtract mediaQuery.size with appBar variable size:
final availableHeight = mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top;
A full example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final PreferredSizeWidget appBar = AppBar(
title: const Text('Personal expensense'),
);
final mediaQuery = MediaQuery.of(context);
final availableHeight = mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: availableHeight * 0.4,
child: Container(),
),
SizedBox(
height: availableHeight * 0.3,
child: Container(),
),
SizedBox(
height: availableHeight * 0.3,
child: Container(),
),
],
);
}
}
It happens to be really useful!
If you have something like a Column, you probably want to use the Expanded widget.
Try this code
Column(
children: [
Container(color: Colors.red, height: 100),
Expanded(
child: Container(color: Colors.yellow, height: 100),
),
Container(color: Colors.red, height: 100),
],
)
From the Expanded-class documentation:
A widget that expands a child of a Row, Column, or Flex so that the
child fills the available space.
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.