Skip to content

Change height of GridView row to fixed height #55290

Closed

Description

@perfoWorks

I've noticed that GridView makes each child a square.. I know we can change dynamically height of grid view row by setting childAspectRatio. That however doesn't make it fixed so when the device rotate then the height changed as well
In this example I want to have each input next to each other and keep it's height same on all devices and all display orientation. (nobody wants their text input change its height)

List<Map<String, dynamic>> list = [
  {'description': 'des1'},
  {'description': 'des2'},
  {'description': 'des3'},
  {'description': 'des4'},
  {'description': 'des5'},
  {'description': 'des6'}
];

class _MaterialScreenState extends State<MaterialScreen> {
  String text = '';

  @override
  Widget build(BuildContext context) {
    double height = MediaQuery.of(context).size.height;
    double width = MediaQuery.of(context).size.width;
    return Scaffold(
        body: Column(
      children: <Widget>[
        Text(text),
        GridView.builder(
            shrinkWrap: true,
            itemCount: list.length,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 5,
                crossAxisSpacing: 5,
                mainAxisSpacing: 5,
                childAspectRatio: width / (height / 4)),
            itemBuilder: (BuildContext context, int index) {
              return TextFormField(
                textAlign: TextAlign.center,
                onTap: () => setState(() {
                  text = list[index]['description'];
                }),
                decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    //isDense: true,
                    contentPadding: EdgeInsets.all(8)),
              );
            }),
      ],
    ))

Activity

  1. VladyslavBondarenko commented on Apr 21, 2020

    @VladyslavBondarenko

    Related to #13777, #29035

  2. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    c: new featureNothing broken; request for a new capability
    on Apr 21, 2020
  3. perfoWorks commented on Apr 21, 2020

    @perfoWorks
    Author

    @VladyslavBondarenko Yes but they all managed to fixed it by setting childAspectRatio.. In my case I can't really do it unless I give up fixed number of items and use maxCrossAxisExtent: which I don't want. I just want the rows to be fixed size and with certain number of items

  4. Zannier7 commented on Oct 2, 2020

    @Zannier7

    aspect ratio does not work for all mobile screen sizes 👎 .

  5. elMuso commented on Oct 3, 2020

    @elMuso

    Hey. Since flutter is open source you can just track the delegate and extend it to use a fixed height. And you can make a custom Delegate !

    As an example, i made SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight in which the aspectratio is ignored, and the fixed height is forced.

    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;
      }
    }
    
    

    to use it simply do

    ...
    GridView.builder(
                shrinkWrap: true,
                itemCount: list.length,
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
                    crossAxisCount: 5,
                    crossAxisSpacing: 5,
                    mainAxisSpacing: 5,
                    height: 48.0, //48 dp of height 
    ...
    
  6. jntagadiad commented on Oct 15, 2020

    @jntagadiad

    final double height;

    thanks a lot!

  7. NanobyteRuata commented on Oct 17, 2020

    @NanobyteRuata

    I made a workaround like

    Wrap(
            children: myListOfData.asMap().map((index, listItem) => MapEntry(index,
                    Container(
                            constraints: BoxConstraints(maxWidth:..., maxHeight:...)
                            child: Card(...)
                    )
            )).values.toList()
    )

    At least in my case, I needed maxWidth but not maxHeight because my Container height depended on it's child contents and I liked it that way.

    I used asMap().map() because I wanted to get the index
    You can make it more simple using just map()

    To scroll u can wrap the Wrap with SingleChildScrollView

  8. jamesdixon commented on Feb 8, 2021

    @jamesdixon

    @ayRatul YES! Nice work and thank you

  9. trailerzone commented on Apr 14, 2021

    @trailerzone

    I made a workaround like

    Wrap(
            children: myListOfData.asMap().map((index, listItem) => MapEntry(index,
                    Container(
                            constraints: BoxConstraints(maxWidth:..., maxHeight:...)
                            child: Card(...)
                    )
            )).values.toList()
    )
    

    At least in my case, I needed maxWidth but not maxHeight because my Container height depended on it's child contents and I liked it that way.

    I used asMap().map() because I wanted to get the index
    You can make it more simple using just map()

    To scroll u can wrap the Wrap with SingleChildScrollView

    Thanks! I was trying to find a solution all of yesterday and this morning I finally found your solution and modified it. Works perfectly! I added SingleChildScrollView to a container above it so I can scroll. We could make a widget for others who are struggling with this or flutter could add this to their core! :)

  10. atn832 commented on May 22, 2021

    @atn832

    How about using mainAxisExtent instead of childAspectRatio? Then the code becomes:

    GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        mainAxisExtent: 100, // <== change the height to fit your needs
      ),
      itemCount: items.length,
      itemBuilder: (context, index) => ...
    );

    https://api.flutter.dev/flutter/rendering/SliverGridDelegateWithMaxCrossAxisExtent/mainAxisExtent.html
    https://api.flutter.dev/flutter/rendering/SliverGridDelegateWithFixedCrossAxisCount/mainAxisExtent.html

  11. kamleshwebtech commented on May 25, 2021

    @kamleshwebtech
    SliverGridDelegateWithFixedCrossAxisCount

    Getting error "A RenderFlex overflowed by 74 pixels on the bottom." Thanks.

  12. mahmedMostafa commented on May 28, 2021

    @mahmedMostafa

    aspect ratio is the worst

  13. ShaheryarMalik007 commented on Sep 16, 2021

    @ShaheryarMalik007

    Taking @elMuso 's answer as a reference
    if someone wants to use the Max Cross Axis Extenet along with fixed height, this might be useful for responsiveness

    class SliverGridDelegateWithMaxCrossAxisExtentAndFixedHeight
    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({
    const SliverGridDelegateWithMaxCrossAxisExtentAndFixedHeight(
    {
    this.crossAxisExtent=0.0,
    required this.maxCrossAxisExtent,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.mainAxisExtent,
    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;

    /// For example, if the grid is vertical, the grid is 500.0 pixels wide, and
    /// [maxCrossAxisExtent] is 150.0, this delegate will create a grid with 4
    /// columns that are 125.0 pixels wide.
    final double maxCrossAxisExtent;
    final double crossAxisExtent;

    /// If null, [childAspectRatio] is used instead.
    final double? mainAxisExtent;

    /// The ratio of the cross-axis to the main-axis extent of each child.
    // final double childAspectRatio;

    bool _debugAssertIsValid(double crossAxisExtent) {
    // assert(crossAxisCount > 0);

    assert(crossAxisExtent > 0.0);
    assert(maxCrossAxisExtent > 0.0);
    assert(mainAxisSpacing >= 0.0);
    assert(crossAxisSpacing >= 0.0);
    // assert(childAspectRatio > 0.0);
    assert(height > 0.0);
    return true;
    

    }

    @OverRide
    SliverGridLayout getLayout(SliverConstraints constraints) {
    assert(_debugAssertIsValid(constraints.crossAxisExtent));

    final int crossAxisCount = (constraints.crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing)).ceil();
    final double usableCrossAxisExtent = math.max(
      0.0,
      constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1),
    );
    final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
    final double childMainAxisExtent = height;
    
    // 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(
    SliverGridDelegateWithMinCrossAxisExtentAndFixedHeight oldDelegate) {
    return
    // oldDelegate.crossAxisCount != crossAxisCount ||
    oldDelegate.crossAxisExtent!=crossAxisExtent||
    oldDelegate.crossAxisExtent!=crossAxisExtent||
    oldDelegate.mainAxisSpacing != mainAxisSpacing ||
    oldDelegate.crossAxisSpacing != crossAxisSpacing ||
    oldDelegate.height != height;
    }
    }

  14. 8 remaining items

  15. emaborsa commented on Sep 1, 2023

    @emaborsa

    I used this sample as well, but I have the problem that the container get stretched by the GridView.

    cal2

    Here my code:

    Stack(children: [
      Container(
        alignment: Alignment.center,
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 15),
          child: Text(
            textAlign: TextAlign.center,
            style: TextStyle(
                color: date.year == year && date.month == month
                    ? Colors.darkGrey
                    : Colors.lightGreyHover),
            date.day.toString().padLeft(2, "0"),
          ),
        ),
      ),
      getEventsContainer(
          events.isNotEmpty ? _getDayEvents(events, date) : [])
    ]),
    
    
    Widget getEventsContainer(List<Event> events) {
      Set<Color?> colors = events.map((e) => e.color).toSet();
    
      if (colors.isEmpty) {
        return Container();
      }
    
      return GridView.builder(
        shrinkWrap: true,
        itemCount: colors.length,
        gridDelegate: FixedHeightSliverGridDelegate(
            height: 3.0, crossAxisCount: colors.length == 1 ? 1 : 2),
        itemBuilder: (BuildContext context, int index) => Container(
          decoration:
              BoxDecoration(color: colors.elementAt(index) ?? Colors.grey),
        ),
      );
    }
    
    class FixedHeightSliverGridDelegate extends SliverGridDelegate {
      const FixedHeightSliverGridDelegate({
        required this.crossAxisCount,
        required this.height,
      }) : assert(crossAxisCount > 0 && height > 0);
    
      final int crossAxisCount;
      final double height;
    
      @override
      SliverGridLayout getLayout(SliverConstraints constraints) {
        final double childCrossAxisExtent =
            constraints.crossAxisExtent / crossAxisCount;
        return SliverGridRegularTileLayout(
          crossAxisCount: crossAxisCount,
          mainAxisStride: height,
          crossAxisStride: childCrossAxisExtent,
          childMainAxisExtent: height,
          childCrossAxisExtent: childCrossAxisExtent,
          reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
        );
      }
    
      @override
      bool shouldRelayout(FixedHeightSliverGridDelegate oldDelegate) {
        return oldDelegate.crossAxisCount != crossAxisCount;
      }
    }
    

    If instead of the GridView i use another container such Text, the cell is not stretched.
    Any hint?

  16. rohandevo commented on Feb 13, 2025

    @rohandevo

    I guess, rather using child aspect ratio, Let child handle the height and width of the container in the grid view. Remove the child aspect ratio. My problems here is, Whenever I make grid. Giving child aspect ratio doesn't help. As if height of smartphone becomes lower the container start spreading more which shouldn't happen. Also if I forcefully make container smaller, It leaves a gap between the container vertically. Just remove the child aspect ratio and let child handle the height and width of the container.

    This this helps but still vertically gap between container spread ->
    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;
    }
    }

  17. added a commit that references this issue on Oct 13, 2025
    22e0750
  18. added a commit that references this issue on Nov 16, 2025
    155a682
  19. added 2 commits that reference this issue on Nov 26, 2025
    3237210
    0971d36
  20. added a commit that references this issue on Dec 2, 2025
    9995d9b
  21. added 2 commits that reference this issue on Dec 2, 2025
    d495bcb
    7bb0484
  22. added a commit that references this issue on Dec 3, 2025
    1869f50
  23. added a commit that references this issue on Dec 10, 2025
    89ef364
  24. github-actions commented on Dec 17, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  25. locked as resolved and limited conversation to collaborators on Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityf: scrollingViewports, list views, slivers, etc.frameworkflutter/packages/flutter repository. See also f: labels.team-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework teamworkaround availableThere is a workaround available to overcome the issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions