1

I am trying to find a way to have two ListView as children of a Row to have matching heights. This means if one ListView is shorter than the other one, then it must stretch until it matches the other ListView's height.

Schematically speaking this is what I have now:

schema

How can I have the green ListView to match the orange's ListView's height?

This is my row at the moment:

Row(
    mainAxisSize: MainAxisSize.max,
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      _buildList(list: listA), // returns a `ListView` wrapped in `Card`
      _buildList(list: listB),        
    ],
  )

I tried setting crossAxisAlignment to CrossAxisAlignment.strech but it causes an error with this message:

A RenderBox object must have an explicit size before it can be hit-tested. Make sure that the RenderBox in question sets its size during layout.

I believe it means that one of the child can't ascertain its height...

Mackovich
  • 3,319
  • 6
  • 35
  • 73
  • Are you referring the background color ? – Md. Yeasin Sheikh Aug 19 '22 at 17:16
  • `IntrinsicHeight` maybe? – pskink Aug 19 '22 at 17:17
  • @YeasinSheikh no I am not referring to the background color. The color is just to help read the schema. In my project, the `ListView`s are wrapped in a material `Card` so it's clearly visible that both lists aren't of the same height. – Mackovich Aug 19 '22 at 17:57
  • Can you test below snippet and share your feedback, that snippet tested inside scaffold body – Md. Yeasin Sheikh Aug 19 '22 at 17:59
  • @pskink I have read on SO that it could be a solution but the documentation strongly advise against it because it's expansive. Where are you suggesting to use it ? On the `Row` itself or on each child (`ListView`) ? – Mackovich Aug 19 '22 at 17:59
  • 1
    check https://youtu.be/aqraU67ms4g or https://youtu.be/ce07f8ClcMQ or similar short intros – pskink Aug 19 '22 at 18:13
  • @pskink thanks, the second video was very good and straight to the point! However it does not seem to work with `ListView`s as I am getting this error: "RenderViewport does not support returning intrinsic dimensions." I have previously tried with `shrinkWrap:true` but then I got the same error except it was for a "RenderShrinkWrappingViewport". I think using `ListView` in my situation is wrong. Working with `Column` instead seems to do the trick though. – Mackovich Aug 19 '22 at 18:25
  • @pskink so yeah, works fine with `Column`. Thank you very much for your solution. If you provide an answer, I'll accept it. – Mackovich Aug 19 '22 at 18:37
  • your welcome, write a self answer then :-) – pskink Aug 19 '22 at 18:59
  • i think you need create list with shinkwrap true, and set list 2 itemExtent height = List 1 height/ List 2 item count – cloudpham93 Aug 19 '22 at 20:14
  • @cloudpham93 how can I know the actual height of either of my list? In one case, List 1 can be bigger than List 2 and in another case, it's the opposite. – Mackovich Aug 19 '22 at 21:06

3 Answers3

1

After following pskink suggestion to use IntrisictHeight, albeit very expensive as per the documentation, I managed to make it work by replacing my ListViews with Columns.

Indeed, as my list are quite short (as explained in my OP), there is no need for ListView with scrolls, animation, recycling, etc.

Using Column was also the only way to make it work with IntrisictHeight anyway.

So this is the solution I retained:

IntrinsicHeight(
    child: Row(
      mainAxisSize: MainAxisSize.max,
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        _buildList(listA),
        _buildList(listB),
    ],
),

Widget _buildList(List<String> list) {
    return Container(
        width: 400,
        margin: const EdgeInsets.only(left: 8, right: 8),
        child: Card(
          elevation: 10,
          child: Column(
           mainAxisSize: MainAxisSize.min,
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
             _SkillType(name: skillType),
             for (final Text in list) Text(text),
          ],
       ),
    );
}

All thanks go to pskink.

Mackovich
  • 3,319
  • 6
  • 35
  • 73
0

You can wrap your ListView with Expanded widget

_buildList({required List<String> list}) {
  return Expanded(
    child: Container(
      color: Colors.cyanAccent,
      child: ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) => ListTile(
          title: Text(list[index]),
        ),
      ),
    ),
  );
}

enter image description here

Also provide ScrollController to listview to avoid getting error

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • Thank you for your suggestion. I tried reproducing your example but I think it does not quite get the result I expect: indeed both `ListView` are now of the same height, however they both seem to stretch to the whole screen. My aim is to have them as short as possible and of the same height. Also, what does `ScrollController` have to do with the error I got? Can you elaborate on that, thanks ! – Mackovich Aug 19 '22 at 18:07
  • While you have multiple scrollable widgets, it will provide an error on console like single controller attached to multiple widget. – Md. Yeasin Sheikh Aug 19 '22 at 18:09
  • You can try `IntrinsicHeight` as pskink mentioned but it is expensive – Md. Yeasin Sheikh Aug 19 '22 at 18:12
0
class Home extends StatefulWidget {
  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  List<String> listData = List.generate(10, (index) => '$index');
  List<String> listData2 = List.generate(5, (index) => '$index');

  List<TextEditingController> listDataCTL = [];

  ScrollController controler = new ScrollController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('AppBar'),
        ),
        body: SingleChildScrollView(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Flexible(
                child: Card(
                  color: Colors.yellow,
                  child: ListView.builder(
                    controller: controler,
                    itemCount: listData.length,
                    shrinkWrap: true,
                    itemExtent: (listData.length < listData2.length) ? listData2.length / listData.length * 50 : 50,
                    itemBuilder: (context, index) {
                      return Container(
                        color: Colors.blue,
                        margin: EdgeInsets.symmetric(vertical: 4),
                        child: Text('Item'),
                      );
                    },
                  ),
                ),
              ),
              Flexible(
                child: Card(
                  color: Colors.green,
                  child: ListView.builder(
                    controller: controler,
                    itemCount: listData2.length,
                    itemExtent: (listData.length > listData2.length) ? listData.length / listData2.length * 50 : 50,
                    shrinkWrap: true,
                    itemBuilder: (context, index) {
                      return Container(
                        margin: EdgeInsets.symmetric(vertical: 4),
                        color: Colors.red,
                        child: Text('Item'),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        ));
  }
}

When listData.length> listData2.length enter image description here

When listData2.length> listData.length enter image description here

UPDATE:

Scaffold(
      appBar: AppBar(
        title: Text('AppBar'),
      ),
      body: Card(
        child: Row(
          children: [
            _buildList(list: listData, compareList: listData2),
            _buildList(list: listData2, compareList: listData),
          ],
        ),
      ),
    )



 _buildList({List<String> list, List<String> compareList, double itemExtent = 50, double spacing = 8}) {
    return Flexible(
      child: GridView.builder(
        shrinkWrap: true,
        padding: EdgeInsets.all(0),
        physics: const NeverScrollableScrollPhysics(),
        itemCount: list.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 1,
          crossAxisSpacing: 0,
          mainAxisSpacing:
              (list.length < compareList.length) ? (((compareList.length - list.length) * itemExtent) + ((compareList.length - 1) * spacing)) / 4 : spacing,
          mainAxisExtent: itemExtent,
        ),
        itemBuilder: (context, index) {
          return Container(
            color: Colors.red.withOpacity(0.3 + ((index * 5) / 100)),
            margin: EdgeInsets.symmetric(vertical: 0),
            child: Text('Item'),
          );
        },
      ),
    );
  }

enter image description here

I think you want this version:

_buildList({List<String> list, List<String> compareList, double itemExtent = 50, double spacing = 8}) {
    return Flexible(
      child: Card(
        child: ListView.builder(
          itemCount: list.length,
          shrinkWrap: true,
          padding: EdgeInsets.only(
            bottom: (list.length < compareList.length) ? (((compareList.length - list.length) * itemExtent) + ((compareList.length - 1) * 0)) : 0,
          ),
          physics: const NeverScrollableScrollPhysics(),
          itemExtent: itemExtent,
          itemBuilder: (context, index) {
            return Container(
              color: Colors.red.withOpacity((index * 5) / 100),
              margin: EdgeInsets.symmetric(vertical: 0),
              child: Text('Item'),
            );
          },
        ),
      ),
    );
  }

enter image description here

cloudpham93
  • 524
  • 1
  • 4
  • Thank you very much for your solution that seems way cheaper than using `IntrisictHeight`, however using `itemExtent` here would be problematic because it makes the children size differently which I don't want to. Also, in my specific case I am using a `Card` as parent for the lists. I wanted both `Card`s in the `Row` to have the same height for perfect symmetry. – Mackovich Aug 22 '22 at 18:04
  • @Mackovich Try new update – cloudpham93 Aug 22 '22 at 18:32
  • @Mackovich try last update – cloudpham93 Aug 22 '22 at 18:47