import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; class ResponsivePair extends StatefulWidget { final List children; final MainAxisAlignment rowMainAxisAlignment; final CrossAxisAlignment rowCrossAxisAlignment; final MainAxisAlignment colMainAxisAlignment; final CrossAxisAlignment colCrossAxisAlignment; const ResponsivePair({ super.key, required this.children, this.rowMainAxisAlignment = MainAxisAlignment.spaceBetween, this.rowCrossAxisAlignment = CrossAxisAlignment.center, this.colMainAxisAlignment = MainAxisAlignment.spaceBetween, this.colCrossAxisAlignment = CrossAxisAlignment.start, }); @override State createState() => _ResponsivePairState(); } class _ResponsivePairState extends State { var _computed = false; final Map _childWidth = {}; var _isRow = true; @override void initState() { super.initState(); } void refreshSize() { if (_childWidth.length != widget.children.length) return; var sum = _childWidth.values.reduce((value, element) => value + element); var width = context.size?.width ?? 0; setState(() { _computed = true; _isRow = sum < width; }); } void handleSizeChange(Size size, int id) { _childWidth[id] = size.width; if (_childWidth.length == widget.children.length) { refreshSize(); } } @override void didChangeDependencies() { super.didChangeDependencies(); setState(() { _computed = false; }); } @override Widget build(BuildContext context) { Widget inner; if (!_computed) { List stackChildren = []; for (var i = 0; i < widget.children.length; i++) { // Only first child is visible stackChildren.add( Opacity( opacity: i == 0 ? 1 : 0, child: WidgetSizeOffsetWrapper( id: i, onSizeChange: handleSizeChange, child: widget.children[i], ), ), ); } inner = Stack( children: stackChildren, ); } else { if (_isRow) { var i = -1; var children = widget.children.map((child) { i++; if (i != 0) { return Flexible(child: child); } return child; }).toList(); inner = Row( mainAxisAlignment: widget.rowMainAxisAlignment, crossAxisAlignment: widget.rowCrossAxisAlignment, children: children, ); } else { inner = Column( mainAxisAlignment: widget.colMainAxisAlignment, crossAxisAlignment: widget.colCrossAxisAlignment, children: widget.children, ); } } return AnimatedSize( duration: const Duration(milliseconds: 150), curve: Curves.linear, alignment: Alignment.topCenter, child: inner, ); } } typedef WidgetSizeChangeCallback = void Function(Size size, int id); class WidgetSizeRenderObject extends RenderProxyBox { final WidgetSizeChangeCallback onSizeChange; final int id; Size? currentSize; WidgetSizeRenderObject(this.onSizeChange, this.id); @override void performLayout() { super.performLayout(); try { Size? newSize = child?.size; if (newSize != null && currentSize != newSize) { currentSize = newSize; WidgetsBinding.instance.addPostFrameCallback((_) { onSizeChange(newSize, id); }); } } catch (e, stack) { debugPrint("$e $stack"); } } } class WidgetSizeOffsetWrapper extends SingleChildRenderObjectWidget { final WidgetSizeChangeCallback onSizeChange; final int id; const WidgetSizeOffsetWrapper({ Key? key, required this.onSizeChange, required this.id, required Widget child, }) : super(key: key, child: child); @override RenderObject createRenderObject(BuildContext context) { return WidgetSizeRenderObject(onSizeChange, id); } }