how to prevent horizontal ListView items from stretching - flutter

I have a horizontal ListView inside a Column.
My ListView is rendering 100 x 100 items that i want to animate their height to 200 later.
I am wrapping my ListView inside a Container that has a height of 200 other wise i get errors and the code will not run
The problem now is that the list items are stretching their height to 200.
Is there a way to prevent that behavior, i tried alignment but it didn't work.
Here's the code so far
Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 200,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
CCard(),
CCard(),
CCard(),
CCard(),
CCard(),
],
),
),
],
));
And the List items
class CCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
height: 100,
width: 100,
color: Colors.red[200],
);
}
}

Try wrapping your Container inside Align or Center, like this :
class CCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
height: 100,
width: 100,
color: Colors.red[200],
),
);
}
}
More info: https://docs.flutter.io/flutter/widgets/Container-class.html

Related

How to set dynamic height for Container Widget in flutter?

I have text widget inside of container and if text is more than 4 lines this is overflowing to bottom of container. How can I set dynamic height for this container that will depends of text lines and avoid bottom overflow ? At the moment container have fixed value but if I remove this value it will disappear.
home component
Widget build(BuildContext context) {
return Container (
child: SliverToBoxAdapter(
child: Container(
height: 180,
margin: EdgeInsets.only(top: 10),
child: CustomeInfoWidget(
infoData: _infoDataList,
),
),
),
)
}
CustomeInfoWidget
Widget build(BuildContext context) {
final double _width = MediaQuery.of(context).size.width;
return Container(
margin: EdgeInsets.only(top: 15, left: 15, right: 15),
child: Stack(
children: [
PageView.builder(
itemCount: widget._infoDataList.length,
controller: _pageController,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: _width - 10,
child: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(widget._infoDataList[index].title,
textAlign: TextAlign.left,
),
Container(
child: Text(
widget._infoDataList[index].content,
),
)
],
);
},
),
],
),
);
}
wrap your container with Singlechildscrollview or set overflow for your Text widget Flutter - Wrap text on overflow, like insert ellipsis or fade
On your home component, you are fixing the height of CustomeInfoWidget. You can remove this.
SliverToBoxAdapter(
child: Container(
// height: 180,// remove this
margin: EdgeInsets.only(top: 10),
child: CustomeInfoWidget(
You can use SliverFillRemaining for PageView.
SliverFillRemaining(
child: PageView.builder(
Or you can wrap your text container with Singlechildscrollview for inner scroll.
You can also calculate the text height and provide it.
How can I get the size of the Text widget in Flutter

Flutter set widget position exactly center of the screen

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,
),
),
),
],
),
),
),
);
}
}

How expanded widget works in flutter?

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.

ShrinkWrap Horizontal ListView inside Column with 'mainAxisSize: MainAxisSize.min' gives error : 'constraints.hasBoundedHeight': is not true

I am trying to display a horizontal ListView which height is given by its children inside a Column which height is also this of its children.
However, I'm getting the following error : 'constraints.hasBoundedHeight': is not true.
Here is a simple snippet to reproduce the error:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
main() => runApp(MaterialApp(
home: Scaffold(
body: MyApp(),
),
));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Vertical1'),
ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: <Widget>[
Container(
height: 100,
width: 100,
child: Text('Horizontal1'),
color: Colors.red,
),
],
),
Text('Vertical2')
],
);
}
}
As you see the children have a defined height so I don't get the issue.
Here is the expected output:
I know I can use a SingleChildScrollView but would just like to understand why it does not work with a ListView.
Just because your child has a size does not mean the ListView will take on that size.
You need to give a height to your ListView because by default its height is infinite.
You get this error because you can't place infinite height in a column,
so you give it a height with the widget Expanded if you want your list to take all the space available.
Here is an example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
Container(color: Colors.red, child: Text('Vertical1')),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 100,
),
child: Container(
color: Colors.green,
child: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: <Widget>[
Container(
height: 100,
width: 100,
child: Text('Horizontal1'),
),
],
),
),
),
Container(color: Colors.red, child: Text('Vertical2'))
],
),
),
);
}
}

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(),
)
)
],
);
}