How to get ordered StaggeredGridView - flutter

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:

Related

Horizontally Center Last Odd Item of Gridview or ListView in Flutter

Horizontally Center Last Odd Item of Gridview or ListView
I tried staggeredGridView but it doesnt have alignment.
Please try the below code :
SizedBox(
height: h * 0.4,
child: StaggeredGridView.countBuilder(
crossAxisCount: 3,
itemCount: 7,
itemBuilder: (BuildContext context, int index) => Container(
child: Center(
child: Container(
height: w / 4,
width: w / 4,
decoration: const BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Center(
child: Text(
index == 6 ? "C" : "$index",
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
)),
staggeredTileBuilder: (int index) =>
StaggeredTile.count(index == 6 ? 3 : 1, 1),
),
),

how to make widget scale up when it selected in Flutter

I have an app that has pageView.builder and it contains 5 stack widget
how can I scale up the widget that user-selected, for example:
if the user scrolls to widget 3, widgets 1 & 2 become smaller than widget 3, and the same if he scrolls to widget 5 or 2
( the middle will become bigger than the widget on both sides)
my code :
Widget build(BuildContext context) {
// ignore: sized_box_for_whitespace
return Container(
height: 320,
child: PageView.builder(
controller: pageController,
itemCount: 5,
itemBuilder: (context, position) {
return _bulidPageItem(position);
}),
);
}
Widget _bulidPageItem(int index) {
//------------------------------------------------------------------------------
// Slide image 🚩
return Stack(
alignment: Alignment.topCenter,
children: [
Container(
margin: const EdgeInsets.only(left: 5, right: 5),
height: 220,
width: 350,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: index.isEven
? const Color(0xFFffd28d)
: const Color(0xFF89dad0),
image: const DecorationImage(
image: AssetImage('images/chineseFood.jpg'), fit: BoxFit.cover),
),
),
//------------------------------------------------------------------------------
// Slide Information 🚩
Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.only(left: 30, right: 30, bottom: 15),
height: 130,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 6,
spreadRadius: 0.7,
offset: const Offset(1, 4))
],
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
//------------------------------------------------
// Slider title
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BigText(
text: 'Chinese side',
),
//----------------------------------------------
// Slider Rating
const SizedBox(height: 10),
Row(
children: [
Wrap(
children: List.generate(
5,
(index) => const Icon(Icons.star,
color: AppColor.mainColor, size: 12),
),
),
const SizedBox(width: 10),
SmallText(text: 4.5.toString()),
const SizedBox(width: 10),
const SmallText(text: '1287 comments'),
],
),
const SizedBox(height: 20),
//----------------------------------------------
// Slider Icons
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
SliderIcons(
color: AppColor.iconColor1,
text: 'Normal',
icon: Icons.circle),
SliderIcons(
color: AppColor.mainColor,
text: '1.7km',
icon: Icons.location_pin),
SliderIcons(
color: AppColor.iconColor2,
text: '32min',
icon: FontAwesomeIcons.clock),
],
),
],
),
),
),
),
],
);
}
}
I think the carousel slider package provides the functionality you need. The package has an example, which exactly describes your issue.
you should try out https://api.flutter.dev/flutter/widgets/ListWheelScrollView-class.html
and for your layout, you can use rotate widget for the horizontal view and of course
you to use rotate widget on the child of the list too.
Save the current page and adjust the content based on that. Like below the current page, _currentPage == index, has smaller margins.
final _pageController = PageController(viewportFraction: 0.8);
int _currentPage = 0;
...
PageView.builder(
controller: _pageController,
itemCount: 5,
itemBuilder: (_, index) =>
Container(
margin: _currentPage == index
? const EdgeInsets.symmetric(vertical: 16)
: const EdgeInsets.symmetric(vertical: 64),
child: Image.network(
'https://picsum.photos/1080/1920?index=$index',
fit: BoxFit.cover,
loadingBuilder: (_, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
}
)
),
onPageChanged: (page){
setState(() {
_currentPage = page;
});
},
)
)

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

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.

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),

Flutter: Creating GridView From Firebase Realtime DB items

Currently, in my app's homescreen I have a firebase_animated_list widget which creates a listView. However, I want to show these items in a GridView because it'll look much better.
Here's my code snippet.👇🏼
body: Column(
children: <Widget>[
Flexible(
child: FirebaseAnimatedList(
query: firebaseRef,
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return InkWell(
child: ListTile(
contentPadding: EdgeInsets.all(7),
title: Text(mynode[index].key),
leading: CircleAvatar(
radius: 30,
child: FittedBox(
child: Text(mynode[index].id),
),
),
trailing: Icon(Icons.play_arrow),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DemoDb(
id: othernode[index].id,
),
),
),
),
);
},
),
),
],
),
This perfectly creates a list of items but how should I change it to a GridView? I tried using GridView.count in place of ListTile widget. but because it was nested inside the firebase_animated_list , Each grid is layed out inside this animated list.
Is there any plugins or libraries that can help me in this? perhaps a code snippet Or if someone can suggest me any better approach to achieve this, it would mean world to me.
Thank You.
try Using StreamBuilder to load the items in a map and then use a Gridview and then you can populate the Gridview with your data.
have a look at the following Example:
Here we have a model of the data that we're trying to pull from firebase.
class Product {
String key;
String cardid;
String cardname;
String cardimage;
int cardprice;
String carddiscription;
Product(this.key,this.cardid,this.cardname,this.cardimage,this.cardprice,this.carddiscription);
}
and here is how to implement a GridView inside a StreamBuilder and populate it with the data:
return StreamBuilder(
stream: FirebaseDatabase.instance
.reference()
.child("Products")
.onValue,
builder: (BuildContext context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
Map<dynamic, dynamic> map = snapshot.data.snapshot.value;
products.clear();
map.forEach((dynamic, v) =>
products.add( new Product(v["key"],v["cardid"] , v["cardname"],v["cardimage"] ,v["cardprice"], v["carddiscription"]))
);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemCount: products.length,
padding: EdgeInsets.all(2.0),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: (){
},
child: Padding(
padding: EdgeInsets.all(5),
child: Container(
width: (screenWidth(context)/2)-15,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
image: DecorationImage(
image: NetworkImage(products[index].cardimage),
fit: BoxFit.cover,
),
),
child: Padding(
padding: EdgeInsets.all(0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
gradient: new LinearGradient(
colors: [
Colors.black,
const Color(0x19000000),
],
begin: const FractionalOffset(0.0, 1.0),
end: const FractionalOffset(0.0, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
products[index].cardname,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500,color: Colors.white),
),
Text('Rs. ${products[index].cardprice}'
,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w200,color: Colors.white),
),
],
),
),
),
), /* add child content here */
),
),
);
},
);
} else {
return CircularProgressIndicator();
}
}),