In Flutter, how to make each Row child as big as the biggest child?

861 Views Asked by At

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...

3

There are 3 best solutions below

3
Md. Yeasin Sheikh On

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

3
cloudpham93 On
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

0
Mackovich On

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.