Search code examples
androidiosflutterlistviewhorizontalscrollview

Flutter - Horizontal ListView with Unbounded height error


When the scrollDirection is set to vertical, it works as expected. The problem is when I set the section.axis to Axis.horizontal so that the ListView displays the widgets horizontally.

This problem wont solve using Flexible or Expanded widget, because the the height of the ListView needs to be defined by the widgets in the list.

As you can see shrinkWrap is also enabled. So I dont know wth is going on here, thanks for helping.

Console says: 'constraints.hasBoundedHeight': is not true. The relevant error-causing widget was: ListView

class SectionWidget extends StatelessWidget {
  final Section section;

  const SectionWidget({@required this.section});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Text(section.title),
        ListView.separated(
          shrinkWrap: true,
          scrollDirection: section.axis,
          physics: section.axis == Axis.vertical
              ? NeverScrollableScrollPhysics()
              : null,
          itemCount: section.itemList.length,
          itemBuilder: (BuildContext context, int index) {
            return Container(
              height: 100,
              width: 100,
              color: Colors.red,
            ); // Just to test if it works
          },
          separatorBuilder: (BuildContext context, int index) {
            double paddingBetween = 10;
            if (section.axis == Axis.horizontal) {
              return SizedBox(width: paddingBetween);
            } else {
              return SizedBox(height: paddingBetween);
            }
          },
        ),
      ],
    );
  }
}

Solution

  • That's because Column or Row gives as much height/width that their children need and ListView takes as much height/width available from its parent.

    To fix this, just wrap ListView in a Container. Like this:

    import 'package:flutter/material.dart';
    
    class SectionWidget extends StatelessWidget {
      final Section section;
    
      const SectionWidget({@required this.section});
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(section.title),
            Container(
              height: section.axis == Axis.horizontal ? 100 : 700,  // I just set these values
              width: section.axis == Axis.horizontal ? 350 : 100,   // to fit with my device.
              child: ListView.separated(                            // If you want to fit for 
                shrinkWrap: true,                                   // all devices, use MediaQuery
                scrollDirection: section.axis,
                physics: section.axis == Axis.vertical
                    ? NeverScrollableScrollPhysics()
                    : null,
                itemCount: section.itemList.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                    height: 100,
                    width: 100,
                    color: Colors.red,
                  ); // Just to test if it works
                },
                separatorBuilder: (BuildContext context, int index) {
                  double paddingBetween = 10;
                  if (section.axis == Axis.horizontal) {
                    return SizedBox(width: paddingBetween);
                  } else {
                    return SizedBox(height: paddingBetween);
                  }
                },
              ),
            ),
          ],
        );
      }
    }
    

    For more info about MediaQuery

    Edit:

    I think using a gridview would be more suited for this. I've remade the build code for ya to suit different children's sizes. The positioning of the children and other stuff I think you can manage.

      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Expanded(
              child: Text(section.title),
            ),
            SizedBox(
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height,
              child: GridView.builder(
                gridDelegate:
                    SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 1),
                shrinkWrap: true,
                scrollDirection: section.axis,
                physics: section.axis == Axis.vertical
                    ? NeverScrollableScrollPhysics()
                    : null,
                itemCount: section.itemList.length,
                itemBuilder: (context, index) {
                  return Column(
                    mainAxisSize: MainAxisSize.min,
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      // Spacer(),
                      Container(
                        margin: EdgeInsets.all(10),
                        height: 100,
                        width: 100,
                        color: Colors.red,
                      ),
                      // Spacer(),
                    ],
                  );
                },
              ),
            ),
          ],
        );
      }