How to set Custom height for Widget in GridView in Flutter?
GridviewDartHeightFlutterGridview Problem Overview
Even after specifying the height for Container GridView, my code is producing square widgets.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> widgetList = ['A', 'B', 'C'];
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(
child: new GridView.count(
crossAxisCount: 2,
controller: new ScrollController(keepScrollOffset: false),
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: widgetList.map((String value) {
return new Container(
height: 250.0,
color: Colors.green,
margin: new EdgeInsets.all(1.0),
child: new Center(
child: new Text(
value,
style: new TextStyle(fontSize: 50.0,color: Colors.white),
),
),
);
}).toList(),
),
),
);
}
}
The output of the code above is as shown on the left. How can I get a GridView with custom height widget as shown on the right?
Gridview Solutions
Solution 1 - Gridview
The key is the childAspectRatio
. This value is use to determine the layout in GridView
. In order to get the desired aspect you have to set it to the (itemWidth
/ itemHeight
). The solution would be this:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> widgetList = ['A', 'B', 'C'];
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
/*24 is for notification bar on Android*/
final double itemHeight = (size.height - kToolbarHeight - 24) / 2;
final double itemWidth = size.width / 2;
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(
child: new GridView.count(
crossAxisCount: 2,
childAspectRatio: (itemWidth / itemHeight),
controller: new ScrollController(keepScrollOffset: false),
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: widgetList.map((String value) {
return new Container(
color: Colors.green,
margin: new EdgeInsets.all(1.0),
child: new Center(
child: new Text(
value,
style: new TextStyle(
fontSize: 50.0,
color: Colors.white,
),
),
),
);
}).toList(),
),
),
);
}
}
Solution 2 - Gridview
Few days ago I came here to find a way to dynamically change height when images are loaded from internet and using childAspectRatio
cannot do that because its apply to all widget in GridView(same height for each).
This answer may help someone who want different height according to each and every widget content:
I found a package called Flutter Staggered GridView by Romain Rastel. Using this package we can do so many things check examples here.
To get what we want we can use StaggeredGridView.count()
and its property staggeredTiles:
and for its value you can map all widget and apply StaggeredTile.fit(2)
.
Example code:
StaggeredGridView.count(
crossAxisCount: 4, // I only need two card horizontally
padding: const EdgeInsets.all(2.0),
children: yourList.map<Widget>((item) {
//Do you need to go somewhere when you tap on this card, wrap using InkWell and add your route
return new Card(
child: Column(
children: <Widget>[
Image.network(item.yourImage),
Text(yourList.yourText),//may be the structure of your data is different
],
),
);
}).toList(),
//Here is the place that we are getting flexible/ dynamic card for various images
staggeredTiles: yourList.map<StaggeredTile>((_) => StaggeredTile.fit(2))
.toList(),
mainAxisSpacing: 3.0,
crossAxisSpacing: 4.0, // add some space
),
);
You can find complete example(copy,paste and run) here.
Solution 3 - Gridview
crossAxisCount
, crossAxisSpacing
and screen width determine width
, and childAspectRatio
determines height
.
I did bit of calculation to figure out relation between them.
var width = (screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount;
var height = width / _aspectRatio;
Full example:
double _crossAxisSpacing = 8, _mainAxisSpacing = 12, _aspectRatio = 2;
int _crossAxisCount = 2;
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
var width = (screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount;
var height = width / _aspectRatio;
return Scaffold(
body: GridView.builder(
itemCount: 10,
itemBuilder: (context, index) => Container(color: Colors.blue[((index) % 9) * 100]),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _crossAxisCount,
crossAxisSpacing: _crossAxisSpacing,
mainAxisSpacing: _mainAxisSpacing,
childAspectRatio: _aspectRatio,
),
),
);
}
Solution 4 - Gridview
just use
childAspectRatio: (1 / .4)
where 1 is width and 0.4 is height customize according to your self full code here
return GridView.count(
crossAxisCount: 2,
childAspectRatio: (1 / .4),
shrinkWrap: true,
children: List.generate(6, (index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
color: Colors.grey[600],
child: Row(
children: [
],
),
),
);
}),
);
Solution 5 - Gridview
All credit goes to @ayRatul on Github for this answer, which is by far the best and simplest I've seen to achieve a fixed item height in a GridView
:
Create your own custom delegate:
class SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight
extends SliverGridDelegate {
/// Creates a delegate that makes grid layouts with a fixed number of tiles in
/// the cross axis.
///
/// All of the arguments must not be null. The `mainAxisSpacing` and
/// `crossAxisSpacing` arguments must not be negative. The `crossAxisCount`
/// and `childAspectRatio` arguments must be greater than zero.
const SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight({
@required this.crossAxisCount,
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
this.height = 56.0,
}) : assert(crossAxisCount != null && crossAxisCount > 0),
assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
assert(height != null && height > 0);
/// The number of children in the cross axis.
final int crossAxisCount;
/// The number of logical pixels between each child along the main axis.
final double mainAxisSpacing;
/// The number of logical pixels between each child along the cross axis.
final double crossAxisSpacing;
/// The height of the crossAxis.
final double height;
bool _debugAssertIsValid() {
assert(crossAxisCount > 0);
assert(mainAxisSpacing >= 0.0);
assert(crossAxisSpacing >= 0.0);
assert(height > 0.0);
return true;
}
@override
SliverGridLayout getLayout(SliverConstraints constraints) {
assert(_debugAssertIsValid());
final double usableCrossAxisExtent =
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
final double childMainAxisExtent = height;
return SliverGridRegularTileLayout(
crossAxisCount: crossAxisCount,
mainAxisStride: childMainAxisExtent + mainAxisSpacing,
crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
childMainAxisExtent: childMainAxisExtent,
childCrossAxisExtent: childCrossAxisExtent,
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
);
}
@override
bool shouldRelayout(
SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight oldDelegate) {
return oldDelegate.crossAxisCount != crossAxisCount ||
oldDelegate.mainAxisSpacing != mainAxisSpacing ||
oldDelegate.crossAxisSpacing != crossAxisSpacing ||
oldDelegate.height != height;
}
}
You can then use your custom delegate as so:
...
GridView.builder(
shrinkWrap: true,
itemCount: list.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
crossAxisCount: 5,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
height: 48.0, //48 dp of height
...
Solution 6 - Gridview
It's working for me.
Example code:
var _crossAxisSpacing = 8;
var _screenWidth = MediaQuery.of(context).size.width;
var _crossAxisCount = 2;
var _width = ( _screenWidth - ((_crossAxisCount - 1) * _crossAxisSpacing)) / _crossAxisCount;
var cellHeight = 60;
var _aspectRatio = _width /cellHeight;
GridView:
GridView.builder(
padding: EdgeInsets.only(
left: 5.0, right: 5.0, top: 10, bottom: 10),
shrinkWrap: false,
itemCount: searchList.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: _crossAxisCount,childAspectRatio: _aspectRatio),
itemBuilder: (context, index) {
final item = searchList[index];
return Card(
child: ListTile(
title: Text(item.name,maxLines: 1,overflow: TextOverflow.ellipsis),
trailing: Container(
width: 15,
height: 15,
decoration: BoxDecoration(
color: item.isActive == true
? Theme.of(context).primaryColor
: Colors.red,
borderRadius: BorderRadius.all(
Radius.circular(50))),
),
onTap: () {
},
),
elevation: 0.5,
);
},
)
Solution 7 - Gridview
mainAxisExtent: 150, // here set custom Height You Want
NOT: dynamic Height like: ( size.height or other) is Not Going to Work
Container(
padding: EdgeInsets.all(10),
color: Colors.grey.shade300,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
mainAxisExtent: 150, // here set custom Height You Want
),
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.white,
),
child: Text('150px'),
);
},
),
),
Solution 8 - Gridview
You can use mainAxisExtent instead of childAspectRatio
GridView.builder(
physics: BouncingScrollPhysics(),
itemCount: resumes.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisExtent: 256,
),
itemBuilder: (_, index) => Container(),
),
Solution 9 - Gridview
Paste this line in GridView.count
childAspectRatio: 2/4,
Solution 10 - Gridview
Inside the GridView, use this code:
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //Number of columns you want
childAspectRatio: (1 / .4) //height & width for the GridTile
),
Solution 11 - Gridview
use
childAspectRatio: .7 (or something you can change it and update your view)
in GridView.builder
use like this
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 10,
childAspectRatio: .7,
),
in GridView.count use like this
GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 10,
childAspectRatio: .7,
)
Solution 12 - Gridview
Well, the first solution I could see everywhere was using childAspectRatio
But aspect ratio was giving different height of the container inside GridView for different screen sizes devices!
But then, I finally got the perfect solution:
Instead of using childAspectRatio
, Use mainAxisExtent
which defines the fixed height of the container
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: CommonDimens.MARGIN_20,
crossAxisSpacing: CommonDimens.MARGIN_20,
crossAxisCount: 2,
mainAxisExtent: 152,
// childAspectRatio: 1.1, DONT USE THIS when using mainAxisExtent
),
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: itemList.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, position) => itemList[position],
),
Solution 13 - Gridview
Gridview build with selected highlighted and other unhighlighted
mainAxisExtent
: 200 (desired height)
GridView.builder(
shrinkWrap: true,
itemCount: _images.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
mainAxisExtent: 200),
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
setState(() {
_selectedTasteIndex = index;
});
},
child: Container(
decoration: BoxDecoration(
color: _selectedTasteIndex == index
? Colors.green
: Colors.white,
borderRadius: BorderRadius.circular(WodlDimens.SPACE10),
border: Border.all(
color: Colors.grey,
width: 1,
),
),
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 30,
width: 30,
child: Image.asset(_images[index]),
),
SizedBox(
width: 10,
),
Text(
_tastes[index],
style: TextStyle(
fontSize: 20,
color: _selectedTasteIndex == index
? Colors.white
: Colors.black,
),
),
],
),
),
);
//return Image.network(images[index]);
},
))