5

I have a Row that has 2 children like this:

 ----------------------
| wide child1 | child2 |
 ----------------------

Is there any way to make each cell be equal in size, so that each cell's width would be equal to the width of the widest cell? Like this:

 --------------------------
| wide child1 |   child2   |
 --------------------------

So the whole Row would take biggestCellWidth * numOfChildren in width.

I couldn't achieve this behavior with built-in widgets and tried to implement MultiChildLayoutDelegate but it also doesn't work since I can't measure children.

Upd:

// in build function
Container(
            height: 48,
            child: Material(
              clipBehavior: Clip.antiAlias,
              borderRadius: BorderRadius.circular(16),
              color: Theme.of(context).colorScheme.primary,
              child: Row(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  // this widgets should be equal in width
                  _buildButton(
                    text: "Review",
                    onTap: _onReviewTap,
                  ),
                  _buildButton(
                    text: "Buy",
                    onTap: _onBuyTap,
                  ),
                ],
              ),
            ),
          );

 Widget _buildButton({
    @required String text,
    @required Function onTap,
    @required EdgeInsets padding,
  }) {
    return InkWell(
      onTap: onTap,
      child: Center(
        child: Text(
          text,
          style: TextStyle(
            color: Theme.of(context).colorScheme.onPrimary,
          ),
        ),
      ),
    );
  }
frozzyk
  • 475
  • 7
  • 16

5 Answers5

11

Wrap your child1 and child2 inside Expanded,

Row(
        children: <Widget>[
          Expanded(
            child: Container(
              color: Colors.amber,
              height: 100,
            ),
          ),
          Expanded(
            child: Container(
              color: Colors.amber,
              height: 100,
            ),
          ),
Ravinder Kumar
  • 7,407
  • 3
  • 28
  • 54
  • It doesn't work if I don't specify Row's width explicitly. I don't want to do that, since all children are `Text` widgets that may have different width depending on language. – frozzyk Nov 27 '19 at 12:26
  • Or if you want to use `Flexible` instead of `Expanded`, then don't forget to use `fit:FlexFit.tight`. to give the equal size to both children. – Ravinder Kumar Nov 27 '19 at 12:26
  • @frozzyk share your piece cod – Ravinder Kumar Nov 27 '19 at 12:31
  • 1
    We can also set "flex" as attribute of Expanded widget for adjusting width ratio of both Expanded widgets. Thanks dear, it works :) – Kamlesh Jun 04 '21 at 10:55
8

I think what you want is a Table? For example:

Table(
  columnWidths: const {
    0: FlexColumnWidth(),
    1: FlexColumnWidth(),
  },
  children: [
    TableRow(
      children: [
        Text(
          "Review",
        ),
        Text(
          "Buy",
        ),
      ]
    ),
  ],
)
James Allen
  • 6,406
  • 8
  • 50
  • 83
2

You mentioned that all your children are Text widget. We can render texts to learn their size (reference), choose maximum width and do layout with MultiChildLayoutDelegate. It's a bit hacky, but will work:

demo

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    final texts = [
      Text('loooooooooooong text'),
      Text('short one'),
      Text('one more'),
    ];
    final children = <Widget>[
      for (int i = 0; i < texts.length; i++) 
        LayoutId(
          id: '$_kLayoutKey$i',
          child: Container(
            color: Color.fromARGB(255, Random().nextInt(255), Random().nextInt(255), Random().nextInt(255)),
            child: texts[i],
          ),
        ),
    ];

    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: CustomMultiChildLayout(
          delegate: _CircularLayoutDelegate(texts, 14),
          children: children,
        )
      ),
    );
  }
}

const String _kLayoutKey = 'test';

class _CircularLayoutDelegate extends MultiChildLayoutDelegate {
  _CircularLayoutDelegate(this.texts, this.fontSize);

  final double fontSize;
  final List<Text> texts;

  double _calcTextWidth(BoxConstraints constraints, Text textWidget) {
    RenderParagraph renderParagraph = RenderParagraph(
      TextSpan(
        text: textWidget.data,
        style: TextStyle(
          fontSize: fontSize,
        ),
      ),
      textDirection: TextDirection.ltr,
      maxLines: 1,
    );
    renderParagraph.layout(constraints);
    return renderParagraph.getMinIntrinsicWidth(fontSize).ceilToDouble();
  }

  @override
  void performLayout(Size size) {
    final textSizes = [
      for (final text in texts) 
        _calcTextWidth(BoxConstraints.loose(size), text),
    ];

    final maxWidth = textSizes.fold<double>(0, (p, v) {
      final textWidth = v;
      return textWidth > p ? textWidth : p;
    });

    final textConstraint = BoxConstraints(
      maxWidth: maxWidth,
      minWidth: maxWidth,
      maxHeight: size.height,
    );

    for (int i = 0; i < texts.length; i++) {
      final String childId = '$_kLayoutKey$i';

      if (hasChild(childId)) {
        layoutChild('$_kLayoutKey$i', textConstraint);

        positionChild(
          childId,
          Offset(maxWidth * i, 0),
        );
      }}
  }

  @override
  bool shouldRelayout(_CircularLayoutDelegate oldDelegate) => oldDelegate.texts != texts;
}
Igor Kharakhordin
  • 9,185
  • 3
  • 40
  • 39
  • Looks like what I want, thanks! To pity it's that tricky to do in flutter. – frozzyk Nov 27 '19 at 13:27
  • 1
    What to do if widgets are not Text? – frozzyk Nov 27 '19 at 13:56
  • 1
    @frozzyk I guess you have to go even lower and work with render objects (for example, like `Flex` class that extends from MultiChildRenderObjectWidget), and it will be harder. You can access children renderboxes within `performLayout` method and learn their size. – Igor Kharakhordin Nov 27 '19 at 14:53
1

Use Expanded with the same flex value to divide the space equally along the main axis. Here you have to understand what is the gist of the main axis. I suggest carefully reading this link: https://api.flutter.dev/flutter/widgets/Column-class.html

For the 'Column' widget, the main axis is vertical-y, while the 'Row' widget has a horizontal-x axis as its main axis. You can check the below code to understand how Expanded is used.

Use DartPad (https://dartpad.dev/?id) to try more example and get to know how Expanded works.

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    
    return Column(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.amber,
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.red,
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.green,
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.blue,
          ),
        ),
      ],
    );
  
  }
}

Output:

enter image description here

Replace above "Column" with "Row", you have this output:

enter image description here

ndnam198
  • 21
  • 5
-1

You can wrap each widget inside Flexible with the same flex number

Rodrigo Bastos
  • 2,230
  • 17
  • 17