Aspectratio of AspectRatio() widget is not respected - flutter

Situation
I have a container with with a column widget as a child. (red widget)
Click here for visual representation
return Container(
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(13.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Header(),
),
Expanded(
child: Pad()
),
],
),
);
The children of the container are Header() (green widget) and Pad() (blue widget). Where Pad() has aspect ratio of 1 / 1 (square).
Header() widget:
class Header extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(13.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("CONTAINER WIDGET WITH TEXT"),
),
);
}
}
Pad() widget:
class Pad extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1 / 1,
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(13.0),
),
child: Center(
child: Text("WIDGET WITH 1/1 ASPECT RATIO")
),
),
);
}
}
Problem summary
If the container with column is given more horizontal space then vertical, then the 1:1 aspect ratio of Pad() (blue widget) is respected and empty space is left out on both sides of the container (red widget). Here is a .gif for visual representation. So far no issue.
However, if the container recieves more vertical space then horizontal, then the 1:1 aspect ratio is is not respected and the square changes into rectangle Here is a visual representation
Expected / desired behaviour
When the container recieves more vertical space then horizontal, the widget with 1:1 aspect ratio should keep it's squared shape. To compensate for the additional space, it should either:
a) Empty space should be kept above and below of the container (same as when more horizontal then vertical space is provided)
b) The Header() widget should expand to compensate for the remaining space
What I tried so far
I suspect that the issue is a direct result of wrapping Pad() with the Expanded() widget. However, without the expanded widget the container with 1:1 aspect ratio would simply overflow, and not shrink.
If I also wrap the Pad() widget with Expanded(), then both of the widgets will take equal space (or provided ratio). However, I want the Header() to take as little space as necessary.

You could also position your Pad in the Center of the Expanded Widget:
Expanded(child: Center(child: Pad())),
With this solution, your whole UI will expand both horizontally and vertically to fill the whole screen while keeping the aspectRatio of 1.
Full source code:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(13.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Header(),
),
Expanded(child: Center(child: Pad())),
],
),
),
);
}
}
class Header extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(13.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("CONTAINER WIDGET WITH TEXT"),
),
);
}
}
class Pad extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1 / 1,
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(13.0),
),
child: Center(child: Text("WIDGET WITH 1/1 ASPECT RATIO")),
),
);
}
}

I solved my issue by putting another 1:1 AspectRatio() widget inside the original one.
class Pad extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1 / 1,
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(13.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AspectRatio(
aspectRatio: 1 / 1,
child: Container(
decoration: BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.circular(13.0),
),
),
),
],
),
),
);
}
}

Related

How to get the status bar height when SystemUiMode is defined to hide the status bar?

I'm overlaying my SystemUiMode, the code is below:
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
I need this SystemUiMode.
Well i have widgets within a SingleChildScrollView (a form let's say). When the keyboard shows up and my content inside the ScrollView is big enough to fill all the available space it hits the top margin of the screen. I wanted a design where my SingleChildScroview had a top padding of the same size of the status bar.
I tried:
To use SafeArea: but it doesn't work, in a first moment my widget fill the entire available space ignoring the status bar height, then it flickers between the expected layout and then goes to filled again. Below is the code:
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
return SafeArea(
child: Scaffold(
backgroundColor: Colors.green,
body: Center(
child: SingleChildScrollView(
child: Center(
child: Container(
width: size.width * .8,
height: size.height * .9,
color: Colors.red,
child: Center(child: TextField()),
),
),
),
),
),
);
}
}
I tried to listen to the changes of the MediaQuery and store the value of the height, but when the keyboard shows up for the first time (sometimes in a second too) it fills the entire space available.
static double topPadding = 0;
setTopPadding(double newPad) {
if (newPad > topPadding) topPadding = newPad;
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
setTopPadding(MediaQuery.of(context).viewPadding.top);
return Scaffold(
backgroundColor: Colors.green,
body: Center(
child: Padding(
padding: EdgeInsets.only(top: topPadding),
child: SingleChildScrollView(
child: Center(
child: Container(
width: size.width * .8,
height: size.height * .9,
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text("A"),
TextField(),
Text("B"),
TextField(),
],
)),
),
),
),
),
);
}
What's the way to get the static height of the status bar?
you can the give Colors.transparent to statusBarColor of the systemOverlayStyle
that makes the statusBar disappear
better to use CustomScrollView Widget instead of SingleChildScrollView...
CustomScrollView(
physics: const BouncingScrollPhysics(), slivers: [
SliverAppBar(
systemOverlayStyle:
const SystemUiOverlayStyle(statusBarColor: Colors.transparent ),
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: 'hello',
background: NetworkImage(imageUrl: networkImage),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(30),
topLeft: Radius.circular(30))),
padding: const EdgeInsets.symmetric(horizontal: 14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: getScreenHeight(10)),
Text(
name,
maxLines: 1,
style: Theme.of(context).textTheme.subtitle1,
textAlign: TextAlign.start,
),]
),)
]);
),
),
},

Flutter: Make Buttons stretch

I having an issue with a Flutter Widget tree I am building:
I Want the Buttons on the Bottom to be bigger and fill all the available space from the text above to the bottom of the screen.
Here my current Code:
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SafeArea(
child: Column(
children: [
UpperDetailsContainer(),
TitleAndPrice(),
//The Row below contains the Two Buttons
Expanded(
child: Row(
children: [
Container(
width: size.width / 2,
child: TextButton(
child: Text("Buy Now"),
style: TextButton.styleFrom(
backgroundColor: kPrimaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(50)
)
)
),
),
),
Container(
width: size.width / 2,
child: TextButton(
child: Text("Description"),
style: TextButton.styleFrom(
backgroundColor: kPrimaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50)
)
)
),
),
),
],
),
)
],
),
);
}
}
I have already tried:
Making the upper widgets smaller
Adding Mainaxisaligment.spacebetween to the surrounding column
Adding Crossaxisaligment.stretch to the Row that contains the buttons
Removing SafeArea bottom / SafeArea as a whole
Setting the height for the buttons manually as an absolute value, but I don't really wanna do this for obvious reasons
What else can i do? And where does that grey bottom Stripe come from?
add crossAxisAlignment: CrossAxisAlignment.stretch to the row, so that it fills all available vertical space
note: you need the Expanded widget so that the constraint your row gets, isn't infinite
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: size.height * 0.7,
child: Container(
color: Colors.amber,
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildBuyNowButton(size),
_buildDescriptionButton(size),
],
),
)
],
),
);
}
Runnable example: https://www.dartpad.dev/4f568d8e0a334d23e7211207081356b4?null_safety=true

Flutter full width Container

I am trying to make Container full width but its not working
void main() {
runApp(MaterialApp(
title: "Practice",
home: Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: Text("My Awesome Border"),
)
],
),
],
),
));
}
this is output in browser
I have couple of more questions
Why color of text is Red and size is big?
How there is a yellow line under text?
Update
Resolved issue with MediaQuery. Here is full working code for future readers.
void main() {
runApp(MaterialApp(
title: "Practice",
home: Scaffold(
body: MyHomeScreen(),
)));
}
class MyHomeScreen extends StatelessWidget {
const MyHomeScreen({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.orange,
border: Border.all(color: Colors.blueAccent)),
child: Text("My Awesome Border"),
)
],
)
],
);
}
}
There are several possibilities
1- Use MediaQuery.of(context).size.width,
Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.orange,
border: Border.all(color: Colors.blue)),
child: Text("My Awesome Border"),
)
Here, make sure that your container has a parent that has context
2- Use double.infinity
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.orange,
border: Border.all(color: Colors.blue)),
child: Text("My Awesome Border"),
)
3- Use FractionallySizedBox widget
Creates a widget that sizes its child to a fraction of the total available space.
Example :
FractionallySizedBox(
widthFactor: 1.0, // between 0 and 1 // 1 for max
heightFactor: 1.0,
child:Container(color: Colors.red
,),
)
4- Use other widgets such as Expanded , Flexible and AspectRatio and more .
Output :
You can write:
Container(
width: double.infinity,
child: Text("My Awesome Border"),
)
I'm used to work with flutter for mobile devices and this kind of error usually happen when we don't have a Scaffold as the base widget. I mean, we can have a SafeArea that has a Scaffold as child, but I don't think we can use a Column as root. So try putting a Scaffold and setting the Column as Scaffold's body. Hope this answer helps you somehow!
By Using the LayoutBuilder a parent widget for your widget and set the constraint.maxWidth to your container to fill the Width.
LayoutBuilder(
builder: (context, constraint){
return Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
width: constraint.maxWidth,
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: Text("My Awesome Border"),
)
],
),
],
);
},
),
Hope it will you to achieve your requirement.
You have missed wrapping the child widget inside the Scaffold Widget like below so that only its showing a red color text and yellow line
void main() {
runApp(MaterialApp(
title: "Practice",
home: CartScreen()
));
}
class CartScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: LayoutBuilder(
builder: (context, constraint){
return Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
width: constraint.maxWidth,
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: Text("My Awesome Border"),
)
],
),
],
);
},
),
);
}
}
Because you have used Column within MaterialPage without Scaffold or CupertinoScaffold. So if you wrap Your Column with Scaffold then you’ll see the Text’s yellow underlines removed and the text will be black.
And I see one other problem with your code, is it the wrong format for dart so it’s not readable so I mean it is not clean code.
Fully formatted code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: buildBody(),
),
);
}
Widget buildBody(){
return Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: Text("My Awesome Border"),
)
],
),
],
);
}
}
You are missing the Scaffold widget. Try using the scaffold widget before the column widget. It will fix the issue

Align ListView's children to top and bottom

TL;DR
Is it possible to align ListView's children like Column's spaceBetween alignment?
Like in this screenshot:
Problem that I tried to solve:
In the beginning I had a Column widget with 2 children. The Column's alignment was set to spaceBetween. It was what I wanted, one widget at the top, one at the bottom. But then when I clicked to input to add credentials, the keyboard caused an overflow issue described in this GitHub issue. So to fix it, I replaced the Column with ListView. But now my children widgets are stuck at the top of the screen.
My full LoginScreen code:
import 'package:flutter/material.dart';
import '../widgets/button.dart';
import '../widgets/input.dart';
import '../widgets/logo.dart';
class LoginScreen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Background(),
Wrapper(),
],
),
);
}
}
class Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Color(0xfff6f6f6),
Colors.white,
],
),
),
);
}
}
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Logo(LogoColor.dummy_black),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20),
child: BottomPart(),
),
),
],
);
}
}
class BottomPart extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Input('User name'),
Input('Password'),
Button(
'Log in',
onPressed,
),
],
);
}
void onPressed() {}
}
You could use ListView's property reverse
reverse: true,
And then change the order of the ListView children.
Find a solution. You should wrap your Column in IntrinsicHeight and ConstrainedBox with minHeight.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: IntrinsicHeight(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
child: Column(
children: [
Container(
height: 100,
width: double.infinity,
color: Colors.yellow,
child: Text('1', style: TextStyle(color: Colors.black)),
),
Spacer(),
Container(
height: 100,
width: double.infinity,
color: Colors.red,
child: Text('2', style: TextStyle(color: Colors.black)),
),
],
),
),
),
);
}
}
Try using SizedBox(height:50.0) // or any other value in b/w Logo widget and Align(bottom part).
return ListView(
children: <Widget>[
Logo(LogoColor.dummy_black),
SizedBox(height: MediaQuery.of(context).size.height/ 3),// you will get value which is 1/3rd part of height of your device screen
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20),
child: BottomPart(),
),
),
],
);
Finally I found out soln to this problem. Use Code :`
Align(
alignment: Alignment.bottomCenter,
child: ListView(
shrinkWrap: true,
children: <Widget>[
// Your Widget Here
],
),
),
you can insert this between logo and Align
Expanded(child: SizedBox(height: 8)),
You could take out the bottom part from ListView, and put it in the last of Stack's children. Your Wrapper could look like:
Widget build(BuildContext context){
var bottomHeight = 120.0;//replaced with your BottomPart's height
return Stack(
children: [
ListView(),//your ListView
Positioned(
top: MediaQuery.of(context).size.height - bottomHeight,
child: SizedBox(
height: bottomHeight,
child: BottomPart(),
)
)
],
);
}

Flutter Layout Row / Column - share width, expand height

I'm still having a bit of trouble with the layouting in Flutter.
Right now I want to have the available space shared between 3 widgets, in a quadrant layout.
The width is evenly shared (this works fine via 2 Expanded widgets in a Row), but now I also want the height to adjust automatically so widget3.height == widget1.height + widget2.height.
If the content of widget3 is larger, I want widget1 and widget2 to adjust their height and vice versa.
Is this even possible in Flutter?
Have a look at IntrinsicHeight; wrapping the root Row should provide the effect you're looking for:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: Text('Rows & Columns')),
body: RowsAndColumns(),
),
);
}
}
class RowsAndColumns extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 100.0),
child: IntrinsicHeight(
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Expanded(
child: Column(children: [
Container(height: 120.0, color: Colors.yellow),
Container(height: 100.0, color: Colors.cyan),
]),
),
Expanded(child: Container(color: Colors.amber)),
]),
),
);
}
}
Adjusting the heights in the containers in the column cause the container on the right to resize to match:
https://gist.github.com/mjohnsullivan/c5b661d7b3b4ca00599e8ef87ff6ac61
I think that you can set the height of the row instead, then you only must to set the height of you column containers, and with the crossAxisAlignment: CrossAxisAlignment.stretch the second row will be expand occupying his parent height:
Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Column(
children: [
Expanded(
// If you don't have the height you can expanded with flex
flex: 1,
child: Container(
height: 50,
color: Colors.blue,
),
),
Expanded(
flex: 1,
child: Container(
height: 50,
color: Colors.red,
),
)
],
),
),
Expanded(
child: Container(
color: Colors.yellow,
),
)
],
)
Since IntrinsicHeight is considered relatively expensive, it's better to avoid it. You can use Table with verticalAlignment: TableCellVerticalAlignment.fill in the largest TableCell.
Please note that if you use .fill in all cells, the TableRow will have zero height.
Table(children: [
TableRow(children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
alignment: Alignment.center,
color: Colors.blue,
child: Text('Widget 1'),
),
Container(
alignment: Alignment.center,
color: Colors.green,
child: Text('Widget 2'),
),
],
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.fill,
child: Container(
alignment: Alignment.center,
color: Colors.orange,
child: Text('Widget 3'),
)),
]),
]),
I'm new to flutter. Please correct me if I'm doing something wrong here. I think It can also be achieved using setState((){}) without using IntrinsicHeight.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
double h1 = 100;
double h2 = 200;
double h3;
setState(() {
h3 = h1 + h2;
});
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 100.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Container(
color: Colors.green,
height: h1,
),
Container(
color: Colors.orange,
height: h2,
)
],
),
),
Expanded(
child: Container(
color: Colors.yellow,
height: h3,
),
)
],
),
),
),
),
);
}
}
Find image here
Code can also be found here: https://gist.github.com/Amrit0786/9d228017c2df6cf277fbbaa4a6b20e83
if you have used the expanded inside Row and facing for full height issue then just wrap the expanded child with Align widget.