Search code examples
flutterflutter-layoutflutter-widget

Set equal size to all children in Row


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

Solution

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