You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
3.9 KiB
Dart

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class ResponsivePair extends StatefulWidget {
final List<Widget> 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<StatefulWidget> createState() => _ResponsivePairState();
}
class _ResponsivePairState extends State<ResponsivePair> {
var _computed = false;
final Map<int, double> _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<Widget> 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);
}
}