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(),
)
)
],
);
}
Related
I want to put a container widget in the middle of the device screen. However, since I used the SizedBox() and SvgPicture.asset() widgets before that, the container does not come right in the middle of the device. How can I do this?
This is my code:
class CenterWidget extends StatefulWidget {
const CenterWidget({Key? key}) : super(key: key);
#override
_CenterWidgetState createState() => _CenterWidgetState();
}
class _CenterWidgetState extends State<CenterWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
const SizedBox(height: 56),
SvgPicture.asset(ImageConstants.instance.logoSvg,
width: (MediaQuery.of(context).size.width - 60) / 2),
Expanded(
child: Center(
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
),
),
],
),
),
),
);
}
}
Use a top center aligned stack, and put the widget you want to be centered within a Positioned.fill widget. You can put the spacer and logo in their own column to keep them arranged vertically:
class _CenterWidgetState extends State<CenterWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Stack(
alignment: Alignment.topCenter,
children: [
Column(
children: [
const SizedBox(height: 56),
SvgPicture.asset(ImageConstants.instance.logoSvg,
width: (MediaQuery.of(context).size.width - 60) / 2),
],
),
Positioned.fill(
child: Center(
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
),
),
],
),
),
),
);
}
}
I have a simple code which produces design like this
This is my code
This is my code for the design:
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATOR'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
],
),
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33)),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
],
),
),
Container(
width: double.infinity,
height: 100,
color: Colors.pink,
)
],
),
);
}
}
Now when I remove the parent expanded widget, I get a design like this:
Here is the code for this design:
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATOR'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
],
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33)),
),
Row(
children: [
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33),)
),
],
),
Container(
width: double.infinity,
height: 100,
color: Colors.pink,
)
],
),
);
}
}
What is the reason for this? Even if I remove for the parent I have added expanded for the child, so shouldn't it be the same?
ReusableCard:
class ReusableCard extends StatelessWidget{
final Color colour;
ReusableCard({#required this.colour});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(20)
),
);
}
}
Basically The Expanded widget will expand to all of the remaining height the parent widget have,
So you in the first code, in your body inside the column we have 4 widgets
Expanded(
child: Row(
),
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33)),
),
Expanded(
child: Row(
),
),
Container(
width: double.infinity,
height: 100,
color: Colors.pink,
)
So lets say that the height of the parent widget is 1000, the
height of the container at the end is fixed so it will always take 100, so 900 is left
The three expanded widget in the column will take the remaining 900, and because you did not specify any flex property in the Expanded widget all of them will receive same height so 900/3 = 300 for each widget.
in the second code inside the column you have
Row(
),
Expanded(
child: ReusableCard(colour: Color(0xff1d1e33)),
),
Row(
),
Container(
width: double.infinity,
height: 100,
color: Colors.pink,
)
again assuming the height available is 1000, the container is fixed height of 100 so 900 is left, now the expanded widget will take all of the available height which is 900 because the rows don't have any fixed height.
the Expanded widget inside the Rows will make the card inside them expand to the parent height, but the parent row does not have any height (0 height) so it will expand to this 0 height which is useless.
always keep it in mind that the Expanded will take the height or width from its parent widget.
How to make soft keyboard covers/overlaps other widgets instead of pushing them up which causes the UI to go crazy and pixels overflow?
I tried with and without Stack()
I tried with and without resizeToAvoidBottomInset: false,
But still no result!
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Stack(
children: <Widget>[
ClipPath(
clipper: CustomBackgroundClipper(),
child: Container(
height: 220.0,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
gradientStart,
gradientEnd
],
),
),
),
),
Container(
height: MediaQuery.of(context).size.height,
child: HomeTopContainer(),
),
],
),
),
);
}
}
Just add this in Scaffold :
resizeToAvoidBottomInset: false,
I donĀ“t know what is inside your HomeTopContainer, but like this way, its working to me:
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: <Widget>[
ClipPath(
child: Container(
height: 220.0,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.green, Colors.blue],
),
),
),
),
Container(
height: MediaQuery.of(context).size.height,
child: Align(
alignment: Alignment.bottomCenter,
child: Column(
children: <Widget>[
Spacer(),
Container(
height: 30,
color: Colors.red,
),
TextField()
],
),
),
),
],
),
);
}
}
I would suggest removing the Align widget completely and adding mainAxisAlignment and crossAxisAlignment properties to your Column widget.
I'm trying to build a List of Card's that contain a Stack widget, the Card + Stack Widget works just fine until I make it the child of a list view, and then the Stack no longer sizes automatically to its largest child. This is causing my view that should be aligned to the bottom right to only be right aligned instead since the bottom of the Stack isn't sized correctly.
I've tried wrapping all the children of the Stack with an Align, but that didn't help either. Positioned has the same results as Align.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stack Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) => Scaffold(
body: ListView(
children: <Widget>[
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
],
),
);
Widget _buildCard(BuildContext context) => Card(
child: Stack(
children: <Widget>[
SizedBox(
height: 200,
child: Container(
color: Colors.black,
),
),
Align(
alignment: Alignment.bottomRight,
child: SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.red,
),
),
),
],
),
);
}
What I'd expect to happen, is the Stack would size correctly around the black container allowing the red container to be positioned at the bottom right of the card.
try to set the alignment on the stack too
alignment: AlignmentDirectional.bottomEnd,
like this one
Widget _buildCard(BuildContext context) => Card(
child: Stack(
alignment: AlignmentDirectional.bottomEnd,
children: <Widget>[
SizedBox(
height: 200,
child: Container(
color: Colors.black,
),
),
Align(
alignment: Alignment.bottomRight,
child: SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.red,
),
),
),
],
),
);
screenshot
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.