Flutter ListView - center items if not expanding beyond screen - flutter

I have a horizontal ListView and depending on the app state, there might be 1-2 items in the list or many.
Now, I want to have the items centered as long as there are only a few available and scrolling is not needed. So far my items always stick to the left side as you can see in the ListView with the lime background color. That one I wanted centered, even if 2 or 3 items are visible, but with more, it shall scroll and then start at the left side with listing the items.
return Container(
color: Colors.lime,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: listItems));
the items itself are this:
Widget getPersonCircleWidget(BuildContext context, Person p) {
return Container(
color: Colors.transparent, //yellow,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: CircularProfileAvatar('',
child: Image(
fit: BoxFit.cover,
image:
UserPhotoManager.singleton.getUserProfilePhoto(p.userId)),
borderColor: colorCirclePhotoBorder,
borderWidth: 2,
elevation: 10,
radius: 40,
onTap: () {
....
}),
),
);
}
Any idea how to solve this?

Just wrap your ListView with Center widget.
Center(
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: listItems.map<Widget>((a) => CircleAvatar(
child: Text(a))).toList(),
),
),
When ListView exceed available room on the screen it will automatically left aligned.
Try it on dartpad.

Related

Why Can't I Define the Height of an Inner Container?

No matter what I do I can't seem to adjust the height of a nested Container in my flutter app. It always seems to inherit the height of the parent Container. Everything I read online said to wrap the inner container with a Center(). I tried this but still ended up with the same issue.
final _recommendations = <String>["Embroidery", "Jewelry Making", "Tennis", "Hiking", "Disc Golf"];
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 16.0),
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _recommendations.length,
itemBuilder: (context, index) {
return Container(
width: 150,
height: 50,
child: Card(
color: Colors.blue,
child: Center(
child: Text(_recommendations[index])
)
)
);
}
)
)
);
Seems to me like the height of ListView is defined by its parent which is Container. So I expect ListView to have a height of 200. Then the height of Card should be defined by its parent container which gives a height of 50. However Card instead takes a height of 200...
Usually, a ListView widget tries to fill all the available space given by the parent element, even when the list items would require less space. You can disable this feature using shrinkWrap parameter of ListView as follow:\
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _recommendations.length,
shrinkWrap:true,
itemBuilder: (context, index) {
return Container(
width: 150,
height: 100,
child: Card(
color: Colors.blue,
child: Center(
child: Text(_recommendations[index])
)
)
);
}
)

how to align listview items top left?

i am trying to align list view items at the top left, but it aligns them in the center. I tried this with another widget, it works fine, but with the listview, it doesn't.
here is my code
Container(
height: height * 30,
// color: Colors.grey[400],
alignment: Alignment.topLeft,
child: ListView.builder(
itemBuilder: (context, i) => ReviewItem(
review: book.reviews[i]['review'],
date: book.reviews[i]['date'],
),
itemCount: book.reviews.length,
),
),
this is the review item
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
timeago.format(DateTime.parse(date)),
style: Theme.of(context).textTheme.headline4.copyWith(
fontSize: width * kFont12Ratio, color: Colors.grey[500]),
),
SizedBox(height: height * 0.5),
Text(review),
Divider(),
],
);
Thanks in advance
By default, ListView will automatically pad the list's scrollable extremities to avoid partial obstructions indicated by MediaQuery's padding. In other words, if you put a widget before the ListView, you should wrap the ListView with a MediaQuery.removePadding widget (with removeTop: true). Like so:
MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(...),
);
I achieved that by adding padding inside the listview builder
padding: EdgeInsets.only(top: 0),
You can use padding instead
Or use Stack with Position (top:0,left:0)
Or use Align( )

Setting a specific height to Flutter's Card widget

I'm trying to expand the height of a given grid of cards, so they are able to fit some more information than they currently do. These cards are wrapped by a GridView.count() that is shrinked, since I'm going to put more things below this widget.
As for now, the cards look like these, in which you can see that one of them overflows the text at the bottom, which is an undesired behavior (especially when I want the cards to have some bottom padding):
Being this the case, I would like to know if it's possible to manually change the card's height. I'm maybe letting this concrete configuration stay and remove some info, since I like the fact that the cards currently maintain their 1x1 proportion, but for curiosity sake, I would like to discover how to do this.
I tried many things, such as wrapping the Card widget with a Container or a SizedBox and manually setting the height, but none of these approaches change anything.
I guess that the problem may be in the GridView itself. This is how it looks:
return FutureBuilder<List<Event>>(
future: new EventsService().getEventsForCoords(),
builder: (context, AsyncSnapshot<List<Event>> snapshot) {
if (snapshot.hasData) {
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
children: generateProximityEventCards(snapshot.data));
} else {
return CircularProgressIndicator();
}
});
As you can guess, the generateProximityEventCards method is the one that prints the Card widgets at the end. This is how the method looks as for now:
List<Widget> generateProximityEventCards(List<Event> eventList) {
// Render each card
return eventList.map((Event ev) {
return Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
margin: EdgeInsets.all(7),
child: SizedBox(
height: 600,
child: Column(
children: <Widget>[
Image(
alignment: Alignment.topCenter,
fit: BoxFit.fitWidth,
image: ev.imageUrl,
height: 110,
width: 200,
),
Container(
child: Text(ev.name),
padding: EdgeInsets.only(left: 10, right: 10),
),
Container(
child: Text(ev.startDate.toString()),
padding: EdgeInsets.only(left: 10, right: 10),
),
Container(
child: Text(ev.address),
padding: EdgeInsets.only(left: 10, right: 10),
),
],
),
));
}).toList();
}
So, in conclussion: how can I change the height of the cards so they can hold more information?
Thanks!
GridView isn't really designed to have tiles of different size. A good option is to use the package flutter_staggered_grid_view.
Now your tile sizes can even be dynamic, check out the code for the gif above!
To automatically fit some variable length text somewhere you can use the package auto_size_text.

Flutter: Container + ListView scrollable

I have a page with container + listView. Now the listView is scrollable, but I would like the whole page to be scrollable. (so if the user scrolls, the container goes 'away')
Column(
children: <Widget>[
Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text("")),
ListView(
children: posts,
),
],
),
Is this possible, and how can it be achieved?
Thanks!
You can wrap the whole page with a SingleChildScrollView
here is an example:
SingleChildScrollView( // <- added
child: Column(
children: <Widget>[
Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text("")),
ListView(
shrinkWrap: true, // <- added
primary: false, // <- added
children: posts,
),
],
),
);
for more info about see this question
What if you don't use Column and use ListView.builder and
according to indexes you show the widget.
For example on 0 index you show the Container.
This way you don't have to use any different widget for scrolling
and as per your requirement the container will scroll with the list.
This way there is no chance that you will get any overflow error
like you were getting while using Column.
Plus point of ListView.builder is that your posts will be created lazily and that is efficient way of creating ListView in Flutter. No matter how many posts you have your UI will not lag.
This at first may look like a hack but according to your requirement that the container must also be able to get scrolled, this solution makes sense to me because Container can be considered as a first element of the list.
Let me know if it works for you or not.
Following is the working code for your reference:
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: posts.length + 1, // +1 because you are showing one extra widget.
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text(""));
}
int numberOfExtraWidget = 1; // here we have 1 ExtraWidget i.e Container.
index = index - numberOfExtraWidget; // index of actual post.
return posts[index];
},
);
}
I hope this works for you, in case of any doubt please comment.
There are multiple ways you can achieve what you want. The only problem is that if you have large number of posts, listview with children posts, as shrinkWrap is set to true, will take time to calculate its required height, for that it will have to populate all of its children.
First
ListView(
children: <Widget>[
Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text("")),
ListView(
physics: NeverScrollableScrollPhysics(),
primary: false,
shrinkWrap: true,
children: posts,
),
),
],
),
Second
SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text("")),
ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
primary: false,
children: posts,
),
],
),
),
use SingleChildScrollView then Container with hard coded container height, then list view builder, that should fix your issue.
I tried the following:
SingleChildScrollView(
child: Container(
height: 1000,
width: 400,
child: ListView(
children: <Widget>[
Container(
color: Colors.white.withOpacity(0.95),
width: 400,
height: 400,
child: Text("")),
ListView(
children: posts,
),
],
),
),
),
This works, but I would like that the container has a dynamic height and width? Is that possible? Thanks!

How to overlap items in a ListView?

I would like to have a drop shadow on each item and each item should overlap the top of the next sibling.
It's probably too late for the original poster, but maybe it will help someone else looking for this:
The best solution I found to this (without resorting to a manually managed stack) is to use an Align widget with a height factor of less than 1. This will cause the next item to overlap with the previous one.
Here are the key elements to make this work:
final elements = ['A', 'B', 'C'];
final padExtend = 16.0;
final media = MediaQuery.of(context);
return ListView.builder(
itemCount: elements.length,
itemBuilder: (BuildContext context, int index) {
final String content = elements[index];
return Align(
child: Container(
width: media.size.width - padExtend / 2.0,
padding: EdgeInsets.all(padExtend),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(color: Colors.grey, spreadRadius: 3, blurRadius: 3, offset: Offset(2, 1)),
],
color: Colors.white,
),
child: Text(content),
),
alignment: Alignment.topCenter,
heightFactor: 0.9,
);
},
);
Since the align would center the elements, a media query is used to set a fixed width.
Wraping your elements with OverflowBox and giving the maxHeight value will achieve this effect.
return SizedBox(
height: 35, //--> list children will be 35 in height
child: OverflowBox(
maxHeight: 50, // --> allowing the child to overflow will cause overlap between elements
child: Container(
height: 50,
child: Text((index + 1).toString()),
),
),
);
use SizedOverflowBox (preferred) or OverflowBox, it is very easy to implement such effects... Flutter YYDS
You could try my package (signed_spacing_flex). It's exactly the same as a normal Column (or Row and Flex). But it lets you set negative spacing which causes its children to overlap.
You can also set which children should be on top when they overlap.
It works with expanded children if you need.
You can use a elevation and padding top to achieve the effect you want :
ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext content, int index) {
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Material(
elevation: 5.0,
child: new Container(
height: 100.0,
),
),
);
})
If you want to overlap your items just use a heightFactor below 1.0
return Align(
alignment: Alignment.topLeft,
heightFactor: 0.9,
child: your item...