Why is Gridview.count not filling out the space with my dynamic content - flutter

I am trying to create a gridview starting from the second column. The first card is just a static card with a button in it. So the second card starting should be dynamic.
All the cards have the same width and height. So basically they all should look like the first card (Add a new dog)
But it's not filling out the space as I expected it would.
Here is part of my code from the body section:
body: Stack(fit: StackFit.expand, children: [
//bg image
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(Images.bgYellow), fit: BoxFit.cover)),
),
//content
SafeArea(
bottom: false,
left: true,
right: true,
top: false,
child: Padding(
padding: EdgeInsets.all(3 * SizeConfig.safeBlockHorizontal),
child: GridView.count(
crossAxisCount: 2,
children: [
//add card
Container(
margin: EdgeInsets.symmetric(
vertical: 1 * SizeConfig.blockSizeVertical,
horizontal: 2 * SizeConfig.blockSizeHorizontal),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(
0, 2), // changes position of shadow
),
],
),
child: FlatButton(
onPressed: null,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
const IconData(0xe901,
fontFamily: 'icDog'),
color: muddyBrown,
size: 20 * SizeConfig.safeBlockHorizontal,
),
SizedBox(height: 5),
Text(
"ADD A NEW DOG",
style: TextStyle(
color: muddyBrown,
fontWeight: FontWeight.bold,
fontSize: 4 *
SizeConfig.safeBlockHorizontal),
)
],
)),
),
//dynamic content
StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return Column(
children: [
...model.state.pets.map((pet) =>
GestureDetector(
onTap: () {
Navigator.pushNamed(
context, petDetailRoute);
},
child: Container(
margin: EdgeInsets.symmetric(
vertical: 1 *
SizeConfig
.blockSizeVertical,
horizontal: 2 *
SizeConfig
.blockSizeHorizontal),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(30),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey
.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 8,
offset: Offset(0,
2), // changes position of shadow
),
],
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
//dynamic data => Photo + Name
children: [
Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
color:
const Color(0xff7c94b6),
image: new DecorationImage(
image: new NetworkImage(
"${pet.photo}"),
fit: BoxFit.cover,
),
borderRadius:
new BorderRadius.all(
new Radius.circular(
50.0)),
border: new Border.all(
color: muddyBrown,
width: 4.0,
),
),
),
SizedBox(height: 5),
Text(
"${pet.name}",
style: TextStyle(
fontSize: 4 *
SizeConfig
.safeBlockHorizontal,
color: muddyBrown,
fontWeight:
FontWeight.bold),
)
],
),
),
))
],
);
}),
],
)
)),
]));

What about simply adding the 'static' card to the 'dynamic' ones and then build one GridView with all of them together?
Widget newDogButton = Card(...);
//dynamic content
StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return Column(
children: [
newDogButton,
...model.state.pets.map((pet) => // ...
That should take care of most of your layout issues automatically.

Because StateBuilder return a widget. How about moving the Whole GridView inside it?
...
child: Padding(
padding: EdgeInsets.all(3 * SizeConfig.safeBlockHorizontal),
child: StateBuilder<PetState>(
observe: () => _petStateRM,
builder: (context, model) {
return GridView.count(
crossAxisCount: 2,
children: [
// The button "ADD A NEW DOG" here,
Container(...),
//dynamic content here
...model.state.pets.map((pet) =>
...
).toList(),
},
),
),

Every grid item in GridView will have same height and width, if you want different dynamic height or width for different items, use flutter_staggered_grid_view, in your case:
StaggeredGridView.countBuilder(
crossAxisCount: 2,
itemCount: 2,
itemBuilder: (BuildContext context, int index) => new Container(
color: Colors.green,
child: new Center(
child: new CircleAvatar(
backgroundColor: Colors.white,
child: new Text('$index'),
),
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(1, index.isEven ? 2 : 1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)

The situation:
To me, this problem is not coming from your Dynamic content but from the height allowed to it to display it's content. Inside the GridView.count constructor, the default childAspectRatio value is 1.0, meaning that the default max height of each child of the GridView is deviceWidth / crossAxisCount (2 in your case).
The problem:
In order for each child to display correctly, it's height must not exceed this ratio (causing your overflowed error).
My opinion:
To solve this problem, I will either replace the dynamic content StateBuilder<PetState> with a static Widget which height will not exceed the ratio OR wrap the dynamic content StateBuilder<PetState> in a SingleChildScrollView to ensure that the overflowed error will not happen and the wrapper can produce the scroll effect to see the entire dynamic content.

Related

how to Make whole page scrollable instead to GridView Builder in flutter?

I am new to flutter and creating a screen with following code:-
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wallpaper/ui/widgets/card_wallpaper.dart';
import '../../providers/anime_provider.dart';
import '../../models/wallpaper.dart';
import '../../providers/wallpaper_provider.dart';
class AnimeDetail extends StatelessWidget {
#override
Widget build(BuildContext context) {
final String id = ModalRoute.of(context).settings.arguments;
final selectedAnime = Provider.of<AnimeProvider>(context).findById(id);
final selectedWallPaper =Provider.of<WallpaperProvider>(context).getByAnime(id);
final appBar = AppBar(
leading: BackButton(
color: Theme.of(context).primaryColor,
),
elevation: 0,
backgroundColor: Colors.transparent,
title: Text(
selectedAnime.title,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontFamily: 'Righteous',
),
),
);
final mediaQuery = MediaQuery.of(context);
final double totalHeight = mediaQuery.size.height -appBar.preferredSize.height -mediaQuery.padding.top -335;
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
height: 300,
child: Card(
elevation: 3.1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: [
Container(
height: 300,
width: double.infinity,
child: ClipRRect(
child: Image.asset(
selectedAnime.imageUrl,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
),
],
),
),
),
SizedBox(height: 10),
Row(
children: [
Container(
height: 25,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 0),
child: Text(
'WallPapers from ${selectedAnime.title}',
style: TextStyle(
fontFamily: 'Righteous',
fontSize: 16,
color: Theme.of(context).primaryColor,
),
),
),
),
],
),
Container(
width: double.infinity,
height: totalHeight,
child: GridView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.5,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: selectedWallPaper[i],
child: CardWallpaper(),
),
padding: const EdgeInsets.all(8.0),
itemCount: selectedWallPaper.length,
),
),
],
),
));
}
}
When I run this app, the gridview is scrollable but the image at the top of it does not scroll,but, I want to scroll whole page but only the grid scrolls even though I am using singlechildscrollview. I tried using expanded on gridview builder but it produces error.How can I make whole page scroll instead of just gridview.builder.
![See the screen here]:https://i.stack.imgur.com/ClDZy.jpg
As you can see when you scroll the page only gridview gets scrolled while top image remains there. I want to scroll the page as whole. Is there any way to determine the height of gridtile?
In your gridview, set the parameter scrollable: NeverScrollablePhysics().

How to get ordered StaggeredGridView

I have been developing a social media app and have an issue with profile page.
Here is the picture of what I need:
And here is the image that I am getting:
As you can see I want that the image sizes be the same for all images in the gallery. However, after I customize the first element of my GridView to contain info about user, I am getting images of different sizes.
Here you can see my code for the Grid:
StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 4,
mainAxisSpacing: 3,
crossAxisSpacing: 3,
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 3),
itemCount: _posts.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Padding(
padding: const EdgeInsets.all(2),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: whiteColor,
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(0.0, 2.0),
blurRadius: 6.0,
),
],
),
padding: EdgeInsets.all(10),
child: Column(
children: [
Row(
children: [
CircleAvatar(
radius: 15,
backgroundImage:
user.profileImageUrl.isEmpty
? AssetImage(
'assets/images/user.png')
: CachedNetworkImageProvider(
user.profileImageUrl)),
SizedBox(width: 10),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
user.name,
style: TextStyle(
color:
Colors.black.withOpacity(0.75),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
SizedBox(height: 5),
_buildStatRow(
'Followers', followerCount.toString()),
_buildStatRow(
'Following', followingCount.toString()),
_buildStatRow(
'Posts', _posts.length.toString()),
_displayButton(user)
],
),
),
);
} else {
Post post = _posts[index - 1];
List<PostView> postViews = [];
postViews.add(PostView(
currentUserId: widget.currentUserId,
post: post,
author: _profileUser,
));
return GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Container(
//height: double.infinity,
color: whiteColor,
child: SingleChildScrollView(
child: Column(
children: postViews,
),
),
),
),
),
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(7)),
child: Image(
image:
CachedNetworkImageProvider(post.imageUrl),
fit: BoxFit.cover,
),
),
),
);
}
},
),
By the way, the very first item of the grid should be of different size (smaller size) so that other images do not align in one line.
I think you want to modify:
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 2 : 3),
into
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index == 0 ? 2 : 3),
In the first case, all the photos in the first column are 2x2 squares, while the ones in the second column are 2x3 rectangles.
In the second case, only the first item, i.e. the header with name/followers/following/post is a square.
Result:

Text align in a box decoration

I have this box decoration used for showing the reviews but I don't know what I should use to align the text properly. This is how it looks right now:
I want the username ("La fottaria to have some space left) I used Align( Alignment(8,3)) but it doesn't change the position.
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return Container(
width: 200,
margin: EdgeInsets.only(
top: 8, bottom: 8, right: 12),
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2,
spreadRadius: 1)
], borderRadius: BorderRadius.circular(4)),
child: Column(
children: [
Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data.documents[index]
.data["avatarUrl"]),
),
Align(
alignment: Alignment(8, 3),
child: Text(snapshot
.data
.documents[index]
.data["name"]),
)
],
),
_buildRatingStars(snapshot.data
.documents[index].data["rating"]),
Text(snapshot
.data.documents[index].data["text"])
],
),
);
}
);
Inside the Row widget, you can use a SizedBox with width property.
It will give some space.
Row(
children: [
CircleAvatar(
backgroundImage:
NetworkImage(snapshot.data.documents[index].data["avatarUrl"]),
),
//insert here
SizedBox(
width: 20.0,
),
Align(
alignment: Alignment(8, 3),
child: Text(snapshot.data.documents[index].data["name"]),
)
],
)
You can remove the Align widget if you want.
Instead of align widget, I suggest you to use Container widget like this:
Container(
margin: const EdgeInsets.only(left: 15),
child: Text(snapshot
.data
.documents[index]
.data["name"]),
)
Use Align
Example:
DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 1,
),
borderRadius: BorderRadius.circular(4),
),
child: Align(
alignment: Alignment.center, // align type
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 15),
child: Text(
title,
style: TextStyle(fontSize: 17, color: Colors.blue),
),
),
),
)
You can use some inbuilt property of rows i.e Main Axis Alignment and Cross Axis Alignment to align its children.For eg:
Row(
mainAxisAligment:MainAxisAlignment.spaceEvenly,
crossAxisAligment:CrossAxisAligment.center,
children: [
CircleAvatar(
backgroundImage:
NetworkImage(snapshot.data.documents[index].data["avatarUrl"]),
),
SizedBox(
width: 20.0,
),
Text(snapshot.data.documents[index].data["name"]),
],
)
You can wrap the text widget inside padding widget also to align according to your needs.

Unable to set dynamic height for a Column involving ListView.builder() flutter

I have the following code where I'm trying to make a column inside which is a container containing title, and another container containing a list of items, that is scrollable horizontally. But I get the following runtime error - "
The following assertion was thrown during performResize():
Horizontal viewport was given unbounded height.
Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand."
Widget horizontalSlider(MediaQueryData mediaQuery){
final List<String> entries = <String>['A', 'B', 'C','D','E'];
return Container(
padding: EdgeInsets.all(0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left:16),
child: Text("Home",style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold),)
),
Container(
child: ListView.builder(
padding: EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index){
return Column(
children: <Widget>[
Container(
height: mediaQuery.size.height/3,
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/$index.jpg'),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(18),
boxShadow: [BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 4,
blurRadius: 4,
),],
),
width: mediaQuery.size.width/1.5,
),
Text("Item ${entries[index]}",style: TextStyle(fontSize: 18),),
],
);
},
scrollDirection: Axis.horizontal,
),
)],
),
);
}
What I expect from this is a heading (i.e. the first container inside the first column), an image of height equal to one-third of screen height and a Text widget at the end. But why do I get the error I cannot understand.
Finally I found the answer from this thread https://github.com/flutter/flutter/issues/18341. I have to replace the second Container with Expanded and it works well. Updated Code
Widget horizontalSlider(MediaQueryData mediaQuery){
final List<String> entries = <String>['A', 'B', 'C','D','E'];
return Container(
padding: EdgeInsets.all(0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(left:16),
child: Text("Home",style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold),)
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index){
return Column(
children: <Widget>[
Container(
height: mediaQuery.size.height/3.5,
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/$index.jpg'),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(18),
boxShadow: [BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 4,
blurRadius: 4,
),],
),
width: mediaQuery.size.width/1.5,
),
Text("Item ${entries[index]}",style: TextStyle(fontSize: 18),),
],
);
},
scrollDirection: Axis.horizontal,
),
)],
),
);
}

Bottom overflowed inside column while listing with Grid.count

I have problem with column, inside Column i have 3 containers. 1st one has picture inside other two has text. For the first container i want to apply height like 100 for example. but max what can i apply is 60 then it giving bottom overflowed. I am using Grid.count to make grid view and listing them with List.genarate.
PS: Wrapping with expanded effecting first container height, and the container does not have fixed height
Here is my code :
Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 90,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.all(Radius.circular(10)),
boxShadow: [
BoxShadow(
color: Theme.of(context).focusColor.withOpacity(0.9),
blurRadius: 4,
offset: Offset(0, 2)),
],
),
width: 90,
child: Hero(
tag: widget.heroTag + widget.product.id,
child: Padding(
padding: const EdgeInsets.all(6.0),
child: CachedNetworkImage(
height: 90,
fit: BoxFit.fill,
imageUrl: this.widget.product.imageUrl,
placeholder: (context, url) => Image.asset(
'assets/img/loading.gif',
fit: BoxFit.cover,
height: 150,
),
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
),
),
Center(
child: Container(
width: 90,
child: Text(
widget.product.name,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
),
Center(
child: Container(
child: Text(
" ${widget.product.salePrice.toString()} Ft ",
style: TextStyle(
color: Colors.blue[800],
fontSize: 13,
fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
),
),
)
],
),
Here i am listing them with grid.count and list.genearte :
GridView.count(
scrollDirection: Axis.vertical,
shrinkWrap: true,
primary: false,
mainAxisSpacing: 15.0,
// crossAxisSpacing: 4.0,
padding: EdgeInsets.symmetric(
vertical: 15),
crossAxisCount:
MediaQuery.of(context)
.orientation ==
Orientation.portrait
? 3
: 3,
children: List.generate(
_con.subCategories
.elementAt(0)
.products
.length, (index) {
return ProductGridItemWidget(
heroTag: 'product-grid' +
0.toString() +
index.toString(),
product: _con.subCategories
.elementAt(0)
.products
.elementAt(index),
onPressed: () {
{
_con.addToCart(_con
.subCategories
.elementAt(0)
.products
.elementAt(index));
}
});
}),
)),
Finally found solution from this post
Give a height to the child of GridView
Just need to write this line of code inside GridView.count
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 4),