Can this Flutter layout be simplified? - flutter

Probably not the best question title but I would like to get some feedback on whether or not my layout could/should be simplified.
I want to get the following layout in Flutter (see image)
in a Container (light green)
I want a a centered central row (dark green) containing 80% of the width and 20% of the height
the row contains three spaced elements (red, blue and orange)
the red one contains a red centered 80% Container
The following code works but feels pretty complicated. Are there more elegant ways to achieve the same thing?
Container(
color: Colors.white,
child: Column(children: [
Expanded(
child: SizedBox.expand(
child: FractionallySizedBox(
widthFactor: 0.8,
heightFactor: 0.2,
alignment: FractionalOffset.center,
child: Container(
child: Row(
children: [
Flexible(
flex:2,
child: SizedBox.expand(child: Container(
color: Colors.red,
child: FractionallySizedBox(
widthFactor: 0.8,
heightFactor: 0.8,
child: Container(color:Colors.yellow),
)
)
),
),
Spacer(),
Flexible(flex:2, child: Container(color: Colors.blue)),
Spacer(),
Flexible(flex:2, child: Container(color: Colors.orange)),
],
))),
),
)
])),

It is not much change but it is something simpler:
Size size = MediaQuery.of(context).size;
return Container(
color: Colors.white,
child: Center(
child: Container(
height: size.height * 0.2,
width: size.width * 0.8,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: size.width * 0.2,
color: Colors.red,
child: FractionallySizedBox(
widthFactor: 0.8,
heightFactor: 0.8,
child: Container(color: Colors.yellow),
)),
Container( width: size.width * 0.2, color: Colors.blue),
Container( width: size.width * 0.2, color: Colors.orange),
],
),
),
));

Related

How to Adjust container height according to the text height in flutter?

I am building a flutter application where I am trying to add a container and it's child text.
I want the container to take as much height as the text requires. But instead it takes the height specified.
Following is my code:
class ChangePassword extends StatefulWidget {
#override
_ChangePasswordState createState() =>
_ChangePasswordState();
}
class _ChangePasswordState extends State<ChangePassword> {
final GlobalKey<ScaffoldState> _scaffoldkey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
key: _scaffoldkey,
drawer: Drawer(
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height * 0.23),
child: DrawerHeader(
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Row(
children: [
Container(
height: MediaQuery.of(context).size.width * 0.1,
width: MediaQuery.of(context).size.width * 0.1,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).primaryColor,
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(imageurl)),
boxShadow: [
BoxShadow(
blurRadius: 25,
color: Colors.black.withOpacity(0.2),
offset: Offset(0, 10),
)
],
),
),
Container(
height: MediaQuery.of(context).size.width * 0.15,
width: MediaQuery.of(context).size.width * 0.5,
padding: EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 15,
color: Colors.lightBlue[900],
fontWeight: FontWeight.bold),
),
Text(email),
SizedBox(
height: 2,
),
Row(
children: [
Icon(
Icons.remove_red_eye_outlined,
size: 12,
),
Text(
'View Profile',
style: TextStyle(
fontSize: 12,
),
)
],
)
],
),
),
],
)),
decoration: BoxDecoration(
color: Colors.white,
),
),
),
],
),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: MediaQuery.of(context).size.height * 0.35,
child: Stack(children: [
Positioned(
height: MediaQuery.of(context).size.height * 0.23,
top: 0,
child: Padding(
padding: EdgeInsets.only(top: 50),
child: Container(
color: Color(0xff002060),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.1,
child: Align(
alignment: Alignment.topCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
_scaffoldkey.currentState.openDrawer();
},
child: new Container(
child: Icon(
Icons.menu,
color: Colors.white,
size: MediaQuery.of(context).size.height *
0.04,
),
padding: new EdgeInsets.all(7.0),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
),
Container(
height:
MediaQuery.of(context).size.height * 0.07,
child: Center(
child: Text(
'Profile',
style: TextStyle(
color: Colors.white,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
),
Icon(
Icons.search,
color: Colors.white,
size: MediaQuery.of(context).size.height * 0.04,
),
],
),
),
),
),
),
Positioned(
top: MediaQuery.of(context).size.height * 0.16,
left: MediaQuery.of(context).size.width * 0.03,
child: Row(
children: [
Container(
width: MediaQuery.of(context).size.height * 0.14,
height: MediaQuery.of(context).size.height * 0.14,
decoration: ShapeDecoration(
shape: CircleBorder(), color: Colors.white),
child: Padding(
padding: EdgeInsets.all(8.0),
child: DecoratedBox(
decoration: ShapeDecoration(
shape: CircleBorder(),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://thumbor.forbes.com/thumbor/fit-in/416x416/filters%3Aformat%28jpg%29/https%3A%2F%2Fspecials-images.forbesimg.com%2Fimageserve%2F5f4ebe0c87612dab4f12a597%2F0x0.jpg%3Fbackground%3D000000%26cropX1%3D292%26cropX2%3D3684%26cropY1%3D592%26cropY2%3D3987',
))),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
),
Column(
children: [
Container(
height: MediaQuery.of(context).size.height * 0.08,
child: Text(
'Bill Gates',
style: TextStyle(
fontSize: 25,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Calibri'),
),
),
],
)
],
),
),
Positioned(
top: MediaQuery.of(context).size.height * 0.24,
left: MediaQuery.of(context).size.height * 0.165,
child: FittedBox(
fit: BoxFit.fill,
child: Container(
height: MediaQuery.of(context).size.height * 0.09,
width: MediaQuery.of(context).size.width * 0.6,
child: Text(
'Bill Gates welcomes you. Welcome to Microsoft. It gives me immense pleasure to announce Josh Hazlewood as it\'s new CEO. It is always immense pleasure to work with them. looking foward to have a great time with him.',
style: TextStyle(
color: Colors.black, fontFamily: 'Calibri'),
),
),
),
),
]),
),
],
)),
],
),
));
}
Following is the image:
I want the container that contains the text below 'Bill Gates' to be wrapped according to the information entered there.
The whole container should decrease as I am planning to add widgets below this customised appbar, so is there any way I can achieve this?
I tried to use flexible but it did not work. Can someone help me with this please?
Use fitted box:-
FittedBox(
fit:BoxFit.fitHeight,
child:Text("Your text"),
),
Do not provide the height of the container and it will adjust automatically.
#Ankit,
Culprit:
As you mentioned, the Container widget is intended to take the width and height you explicitly specify, so it does what you tell it to do.
What you should have done in the above case is avoid specifying the height for that Container which contains the Text widget as follows.
Change this code section from:
Positioned(
top: MediaQuery.of(context).size.height * 0.24,
left: MediaQuery.of(context).size.height * 0.165,
child: FittedBox(
fit: BoxFit.fill,
child: Container(
height: MediaQuery.of(context).size.height * 0.09,
width: MediaQuery.of(context).size.width * 0.6,
child: Text(
'Bill Gates welcomes you. Welcome to Microsoft. It gives me immense pleasure to announce Josh Hazlewood as it\'s new CEO. It is always immense pleasure to work with them. looking foward to have a great time with him.',
style: TextStyle(
color: Colors.black, fontFamily: 'Calibri'),
),
),
),
),
To the following code, where I have removed specifying the height property of the container:
Positioned(
top: MediaQuery.of(context).size.height * 0.24,
left: MediaQuery.of(context).size.height * 0.165,
child: FittedBox(
fit: BoxFit.height,
child: Container(
width: MediaQuery.of(context).size.width * 0.6,
child: Text(
'Bill Gates welcomes you. Welcome to Microsoft. It gives me immense pleasure to announce Josh Hazlewood as it\'s new CEO. It is always immense pleasure to work with them. looking foward to have a great time with him.',
style: TextStyle(
color: Colors.black, fontFamily: 'Calibri'),
),
),
),
),
By doing that, and wrapping the Container within a FittedBox, that varying text length Text widget will cause it to adjust its height accordingly.
Following part is important here:
child: FittedBox(
fit: BoxFit.height,

What error am I making trying to get my desired UI in Flutter/Dart?

I have tried to achieve the effect shown in the highlighted portion of the image with the following code, but this produces counter-intuitive and wrong results.
Just to avoid confusion, I am talking about the effect of the middle of the smaller containers being aligned with the bottom of the large orange-ish container
My code is as follows:
Size size = MediaQuery.of(context).size;
double height = size.height;
double width = size.width;
return Column(
children: [
Expanded(
flex: 3,
child: Container(color: Colors.white),
),
Positioned(
top: height * (3 / 5) - height * 0.15 / 2,
child: Container(
color: Colors.green,
height: height * 0.15,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: height * 0.15,
width: width * 0.45,
color: Colors.red,
),
Container(
height: height * 0.15,
width: width * 0.45,
color: Colors.blue,
)
],
),
),
),
Expanded(
flex: 5,
child: Container(color: Colors.black),
),
],
);
How can I improve upon my code?
You will need to use the Stack widget to achieve this result.
Stack(children: [
Container(
height: 230,
decoration: BoxDecoration(
color: Colors.orange,
)),
Column(children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(22))),
Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(22))),
Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(22))),
]),
),
])
])

How to make the bar chart starts from the bottom to top in flutter?

I would like to make the bar chart starts from bottom to top in flutter because my bar chart is starting from top to down and this is leaving a bad impact for the users. So, how to can I make the bar chart starts from the bottom to top in flutter?
Bellow are two widgets of the bar chart, I just spread them into two different files.
print('build() ChartBar');
return LayoutBuilder(builder: (ctx, constraints) {
return
Column(
children: <Widget>[
Container(
height: constraints.maxHeight * 0.15,
child: FittedBox(
child: Text('\$${spendingAmount.toStringAsFixed(0)}'),
),
),
SizedBox(
height: constraints.maxHeight * 0.05,
),
Container(
height: constraints.maxHeight * 0.6,
width: 10,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1.0),
color: Color.fromRGBO(220, 220, 220, 1),
borderRadius: BorderRadius.circular(10),
),
),
FractionallySizedBox(
heightFactor: spendingPercentageOfTotal,
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10),
),
)),
],
),
),
SizedBox(
height: constraints.maxHeight * 0.05,
),
Container(
height: constraints.maxHeight * 0.15,
child: FittedBox(
child: Text(label),
),
)
],
);
});
}
}
Widget build(BuildContext context) {
print('build() Chart');
return Card(
elevation: 6,
margin: EdgeInsets.all(10),
child: Container(
padding: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: groupedTransactionValues.map(
(data) {
return Flexible(
fit: FlexFit.tight,
child: ChartBar(
data['day'],
data['amount'],
totaLSpending == 0.0
? 0.0
: (data['amount'] as double) / totaLSpending));
},
).toList(),
),
),
);
}
}
you can use this code below. Flutter default is from top to bottom. You can change this by using Align widget.
Align(
alignment: Alignment.bottomCenter,
child: FractionallySizedBox(....),
)
add this into Stack widget
alignment: AlignmentDirectional.bottomStart,
the easy way is to use dependencies
https://pub.dev/packages/fl_chart

I want to divide my row into 1/4 ratio in Flutter

I want to divide my row in flutter into 4 columns with the same width. So far, the solution I came up with is this,
child: Row(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.greenAccent),
),
Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.yellow),
),
Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.blue),
),
Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.white),
),
],
),
Is there any alternative to this. Also dividing the row into 1/3 ratio would not work in this method.
You can achieve this by using an Expanded Widget
Check the code below:
It works perfectly well.
1) For 1/3 ratio
Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.greenAccent),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.yellow),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.blue),
),
),
],
),
1) For 1/4 ratio
Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.greenAccent),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.yellow),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.blue),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.25,
decoration: BoxDecoration(color: Colors.white),
),
),
],
),
See output below:
I hope this answers your questions.
You can use Expanded or Flexible, by default it has flex: 1, and you can change the values depending on your need.
Row(
children: [
Expanded(
flex: 1, // you can play with this value, by default it is 1
child: Child1(),
),
Expanded(
flex: 1,
child: Child2(),
),
Expanded(
flex: 1,
child: Child3(),
),
Expanded(
flex: 1,
child: Child4(),
),
],
);
As it's not mentioned in the above solutions. Flex is the proportion to be occupied in the available widget. If you're using 2 Expanded widgets one with flex 1 and the other with flex 3 then available space will be occupied as 1/4 by the first Expanded widget and 3/4 by the other.

Flutter how can i round a container from middle

I have 2 containers I need to round 1 container from mid-bottom and other from mid-top so it will create a circle between 2 containers so I can add Text here. Can anyone please tell how it's possible
body: Container(
child: Column(
children: <Widget>[
SizedBox(height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height - MediaQuery.of(context).padding.top) *
0.05,
),
Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
color: Colors.red,
height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height - MediaQuery.of(context).padding.top) *
0.40,
child: Column(
children: <Widget>[
Text('10%'),
Text('Say goodbye')
],
),
),
),
Container(
height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height - MediaQuery.of(context).padding.top) *
0.1,
child: Text('OR'),
),
Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height - MediaQuery.of(context).padding.top) *
0.40,
color: Colors.blue,
child: Column(
children: <Widget>[
Text('10%'),
Text('Say goodbye')
],
),
),
),
SizedBox(height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height - MediaQuery.of(context).padding.top) *
0.05,
),
],
),
),
Like this
You can round a container by using a BoxDecoration and applying a BoxShape.circle to it:
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
AppBar appBar = AppBar(
title: Text('Demo'),
);
double stackHeight = MediaQuery.of(context).size.height - 105;
double stackWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: appBar,
body: Stack(
children: [
Container(
height: stackHeight * 0.5,
width: stackWidth,
color: Colors.blue,
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: stackHeight * 0.5,
width: stackWidth,
color: Colors.red,
),
),
Align(
alignment: Alignment.center,
child: Container(
width: stackWidth,
height: stackHeight * 0.015,
color: Colors.black,
),
),
Align(
alignment: Alignment.center,
child: Container(
width: stackWidth,
height: stackHeight * 0.15,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
),
child: Align(
alignment: Alignment.center,
child: Text(
"OR",
style: TextStyle(color: Colors.white),
),
)
),
),
],
)
);
}
This produced the following screen:
you can use Stack Widget to place widgets
here is exactly what you need
body: Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
color: Colors.amber,
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.48,
color: Colors.blue,
),
Container(
height: MediaQuery.of(context).size.height * 0.04,
color: Colors.black,
),
Container(
height: MediaQuery.of(context).size.height * 0.48,
color: Colors.red,
)
],
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Center(
child: Container(
height: MediaQuery.of(context).size.height * 0.20,
width: MediaQuery.of(context).size.width * 0.20,
decoration: BoxDecoration(
color: Colors.black, shape: BoxShape.circle),
),
),
],
),
Center(
child: Text(
"QR",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 24.0),
),
)
],
),
Hope it will help
As told before the solution may be the Stack Widget. Below a possible solution.
You can change the color of the middle part with the same color of the background in order to obtain the desired effect.
Stack(
alignment: Alignment.center,
children: <Widget>[
Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * 0.9,
color: Colors.blue,
height: 200),
Container(
width: MediaQuery.of(context).size.width * 0.9,
height: 20,
color: Colors.black),
Container(
width: MediaQuery.of(context).size.width * 0.9,
color: Colors.red,
height: 200),
]),
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.black,shape: BoxShape.circle),
child: Center(child: Text("or")),
),
],
);