How to set Custom height for Widget in GridView in Flutter?
Asked Answered
C

18

231

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?

output Required Output

Chevaldefrise answered 23/1, 2018 at 15:19 Comment(2)
Possible duplicate of GridView containing Cards doesn't calculate height correctlyWeed
@Weed I have to show grid in 1:1 (width:height) ratio but height should have additional 100 pixels extra. eg. How can i do? Kindly suggest. Thanks.Inbreed
A
358

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(),
        ),
      ),
    );
  }
}
Accountant answered 23/1, 2018 at 20:46 Comment(4)
The GridView doesn't have the property childAspectRatio unless you're doing the count way :-/Djerba
@OliverDixon if you're using GridView.builder and SliverGridDelegateWithFixedCrossAxisCount then childAspectRatio is on the gridDelegateGarfish
how to make this height dynamic?Agnew
Dynamic height items - Staggered grid Normal static height items - GridviewIndreetloire
S
56

The best answer you'll find

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: [

          ],
        ),
      ),
    );
  }),
);

enter image description here

Sherfield answered 27/5, 2021 at 3:2 Comment(0)
U
30

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.

Unformed answered 6/1, 2019 at 8:22 Comment(2)
staggeredTiles is not defined Sir.Wyman
@NicholasJela syntax may have been changed in latest package. Checkout examples for latest: github.com/letsar/flutter_staggered_grid_view/tree/master/…Unformed
P
25

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 
...
Penutian answered 8/2, 2021 at 15:40 Comment(2)
I have to show grid in 1:1 (width:height) ratio but height should have additional 100 pixels extra. eg. How can i do? Kindly suggest. Thanks.Inbreed
Once you've been doing GridViews for a while, you'll realize this should be adopted by the Flutter team and made a widget of the week.Histo
G
21

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,
      ),
    ),
  );
}
Gales answered 28/7, 2019 at 4:19 Comment(0)
O
18

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

enter image description here

Oviposit answered 21/11, 2021 at 8:44 Comment(1)
best and simple answerEquipment
Q
11

enter image description here

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,
                            );
                          },
                        ) 
Quenelle answered 30/3, 2020 at 4:11 Comment(0)
B
10

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]);
                    },
                  ))
Bondy answered 11/5, 2022 at 18:16 Comment(1)
you're damn right about that..Maria
M
8

You can use mainAxisExtent instead of childAspectRatio

    GridView.builder(
                physics: BouncingScrollPhysics(),
                itemCount: resumes.length,
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisExtent: 256,
                ),
                itemBuilder: (_, index) => Container(),
              ),
Maidenly answered 15/11, 2021 at 13:45 Comment(1)
This was the answer i was actually looking for, it actually worked like magicTransparency
A
6

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],
      ),
Apul answered 11/5, 2022 at 10:3 Comment(0)
F
5

You need to use childAspectRatio property in the gridDelegate of a GridView:

gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                childAspectRatio: 2/1.6  // try to fiddle with this ratio
                crossAxisCount: 2,
                crossAxisSpacing: 15,
                mainAxisSpacing: 15,
              ),
Flaunty answered 3/12, 2022 at 16:51 Comment(0)
R
4

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,
)
Raney answered 19/4, 2022 at 19:44 Comment(0)
P
1

Inside the GridView, use this code:

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //Number of columns you want
childAspectRatio: (1 / .4) //height & width for the GridTile
),
Profession answered 7/8, 2021 at 2:5 Comment(0)
D
1

Paste this line in GridView.count

childAspectRatio: 2/4,

Dulse answered 8/11, 2021 at 12:44 Comment(2)
Explain what it does and where you got the answer?Anhedral
The childAspectRatio: 2/4 means 2 parts of width and 4 parts of height . I've used it in my code and it's working pretty fine . You can also use childAspectRatio in gridview.count according to your requirement to get the desired results.Dulse
M
1

The only built-in easy solution is, to use childAspectRatio and set it to something like 1 / .6 or 1 / .7 depending on your widget size. By default, Flutter GridView.count sets height and width the same size and it causes excess padding if the design requires different shape output. With childAspectRatio way we can assign half value of the width to the height and get what we want to achieve.

GridView.count(
                      childAspectRatio: (1 / .7), // this line is the one determines the width-height ratio
                      crossAxisCount: 2,
                      crossAxisSpacing: gScreenWidth / 40,
                      mainAxisSpacing: gScreenWidth / 40,
                                           children: List.generate(
                          yourList.length, (index) {
                        return Center(
                            child: Container(
                               child: Text('Hello World. Finally I fixed the height issue'))),
},
)),
Moor answered 10/11, 2022 at 6:31 Comment(0)
W
0

This question was one of my problems, But I found that if we put the repetitive widget in the SingleChildScrollView widget, the height of the repetitive widget will be in the minimum size!

that's it!

Wade answered 5/10, 2022 at 16:24 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Multiversity
M
0

You can use Auto Height Grid View to easily manage the grid view.

https://pub.dev/packages/auto_height_grid_view

Myrtismyrtle answered 18/10, 2023 at 8:20 Comment(0)
Q
0

So because I had some issues with grid view and I really didn't like aspect ration (it does not work properly in flutter web) I came up by creating "custom grid view". You can edit these widget with your needs. Each child takes the height that it needs, but you can edit it and add a default height or something.

CustomGridView:

import 'package:flutter/material.dart';

class CustomGridView extends StatelessWidget {
  const CustomGridView({
    super.key,
    required this.crossAxisCount,
    required this.length,
    required this.widgetOfIndex,
    this.crossAxisSpacing = 0,
    this.mainAxisSpacing = 0,
    this.physics,
    this.shrinkWrap = false,
  });

  final int crossAxisCount;
  final int length;
  final double crossAxisSpacing;
  final double mainAxisSpacing;
  final Widget Function(int index) widgetOfIndex;
  final ScrollPhysics? physics;
  final bool shrinkWrap;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      physics: physics,
      shrinkWrap: shrinkWrap,
      itemCount: (length / crossAxisCount).ceil(),
      itemBuilder: (BuildContext context, int listIndex) {
        return Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: List<Widget>.generate(
            crossAxisCount,
            (int index) {
              return Expanded(
                child: Padding(
                  padding: EdgeInsets.only(
                    right: index < crossAxisCount - 1 ? crossAxisSpacing / 2 : 0,
                    left: index > 0 ? crossAxisSpacing / 2 : 0,
                    bottom: mainAxisSpacing,
                  ),
                  child: listIndex * crossAxisCount + index < length ? widgetOfIndex(listIndex * crossAxisCount + index) : const SizedBox(),
                ),
              );
            },
          ),
        );
      },
    );
  }
}

This is how you can call it:

CustomGridView(
          crossAxisCount: 2,
          crossAxisSpacing: 16,
          length: _apartments.length,
          widgetOfIndex: (int index) {
            return YourWidget(_apartments[index]);
          },
        ),

For Sliver grid view you can get this,

CustomSliverGridView:

import 'package:flutter/material.dart';

class CustomSliverGridView extends StatelessWidget {
  const CustomSliverGridView({
    super.key,
    required this.crossAxisCount,
    required this.length,
    required this.widgetOfIndex,
    this.crossAxisSpacing = 0,
    this.mainAxisSpacing = 0,
  });

  final int crossAxisCount;
  final int length;
  final double crossAxisSpacing;
  final double mainAxisSpacing;
  final Widget Function(int index) widgetOfIndex;


  @override
  Widget build(BuildContext context) {
    return SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int listIndex) {
          return Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: List<Widget>.generate(
              crossAxisCount,
              (int index) {
                return Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(
                      right: index < crossAxisCount - 1 ? crossAxisSpacing / 2 : 0,
                      left: index > 0 ? crossAxisSpacing / 2 : 0,
                      bottom: mainAxisSpacing,
                    ),
                    child: listIndex * crossAxisCount + index < length ? widgetOfIndex(listIndex * crossAxisCount + index) : const SizedBox(),
                  ),
                );
              },
            ),
          );
        },
        childCount: (length / crossAxisCount).ceil(),
      ),
    );
  }
}

Finally you can get the custom sliver grid view like this:

 CustomSliverGridView(
                    crossAxisCount: 2,
                    crossAxisSpacing: 16,
                    length: _apartments.length,
                    widgetOfIndex: (int index) {
                      return YourWidget(_apartments[index]);
                    },
                  )
Quixote answered 12/2 at 15:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.