5
\$\begingroup\$

I create generic Row to handle almost 90 percent of the basic feature.

I need your feedback to make it more flexible and extensible

/// A generic Row widget that allows customization of alignment, spacing, and padding.
///
/// - [children]: The list of widgets to display in the row.
/// - [mainAxisAlignment]: How the children should be aligned horizontally.
/// - [crossAxisAlignment]: How the children should be aligned vertically.
/// - [spacing]: The amount of space to add between children.
/// - [padding]: The padding to apply around the row.
/// - [useExpanded]: Whether to wrap children in Expanded to share space equally.
/// - [flexValues]: Optional flex values for each child when [useExpanded] is true.
///
/// Edge Cases and Limitations:
/// No Support for Flexible Ratios
/// If children is empty, the widget will still render an empty Row
/// It doesn't support multi sizes and multi ratio at the moment..
class GenericRow extends StatelessWidget {
  final List<Widget> children;
  final MainAxisAlignment mainAxisAlignment;
  final CrossAxisAlignment crossAxisAlignment;
  final double spacing;
  final EdgeInsets padding; // Padding around the row
  final bool useExpanded; // if set width will no effect on it

  const GenericRow({
    super.key,
    required this.children,
    this.mainAxisAlignment = MainAxisAlignment.center,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.spacing = 0.0,
    this.padding = EdgeInsets.zero,
    this.useExpanded = true,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: padding,
      child: Row(
        mainAxisAlignment: mainAxisAlignment,
        crossAxisAlignment: crossAxisAlignment,
        children: [
          for (int index = 0; index < children.length; index++) ...[
            if (useExpanded) Expanded(child: children[index]) else children[index],
            if (index < children.length - 1) SizedBox(width: spacing),
          ],
        ],
      ),
    );
  }
}

I also added not possible items in comments.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

First, I would like to encourage you to take the initiative. However, my advice is not to try reinventing the wheel. Meaning: the row widget already does what you need: it defies the purpose to abstract something that is already there:

Row(
      spacing: 44, /// double here
      children: [],
      mainAxisSize: MainAxisSize.min, /// If you use this, mainAxisAlignement ///will be redundant, because the row will wrap exactly around its children. 
      mainAxisAlignment: MainAxisAlignment.spaceBetween, 
/// there multiple varients of this enum, spaceBetween/ spaceEvently/ ///spaceAround / start /end ...
      crossAxisAlignment: CrossAxisAlignment.center,
/// this controls how your children lay themselves according the crossAxis  //// (for the Row CrossAxis is vertical / for the column CrossAxis is : horizontal )
    );

Now why your approach is bad:

Expanded widget checks the parent constraints regularity and rebuilds the children, whenever building your app to target mobile screens (that have fixed constraints) it will only cause heavy rebuilds at the very start, but if you decide to target other platforms like desktop or web (where you could resize the screen) every time you resize the window the expanded widget will invoke its rebuild, and that could look bad and cause shaky UI.

Plus, your implementation assumes that row uses Expanded widget under the hood, however it doesn't. so please only use Expanded, Flexible, IntrinsicHight ... widgets very wisely because they are heavy in terms of rendering.

One more point: you should understand how the flutter engine works before you do things like these : The flutter render Object has 2 protocols that it implements when laying out the children:

  • renderBox protocol: used for fixed positioning: child passes size to its parent => parent passes constraints to its child and the paint begins from the original paint offset: by default it is Offset(0,0) and you can change it if you work directly with Renderobjects. ==> when we use stack, Row, column ... we are implementing this protocol, meaning we should try to pass a child with a fixed size as much as possible because it will make the layout widget's life easier. When we wrap the child with Exapnded, we mean: hey parent, we want you take as much of available space and ignore the child's size because we are greedy, the problem is that the available space can be unlimited, or it can be variable (in this case we will have rebuilds everytime the available space changes)

  • render sliver protocol: is implemented in scrollable areas: like listview, pageview .... and customScrollView when you are working with slivers.instead of working with paint offset, it works with scroll offset.

\$\endgroup\$
2
  • \$\begingroup\$ by shaky UI i mean: when using multiple expanded widgets having different flex factors, this will cause shaky UI sometimes when the engine can not measure the constraints accurately, it happened to me while using Flutter for desktop a year ago, and now I'm doing my best to avoid it. \$\endgroup\$ Commented Mar 22 at 0:48
  • 1
    \$\begingroup\$ also see this question please codereview.stackexchange.com/q/296012/288555 \$\endgroup\$ Commented Apr 29 at 10:07

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.