I want to force a child widget to have a max aspect ratio of e.g. 2.0: in other words, it is no wider than 2:1, but is as tall as possible. So in a tall skinny parent it would behave like SizedBox.expand(child: child), and in a short wide parent it would behave like AspectRatio(aspectRatio: 2.0, child: child).
Is there a widget that does this, like ConstrainedAspectRatio(maxAspectRatio: 2.0, child: child)? Or do I have to choose between (1) using a LayoutBuilder or (2) writing a custom SingleChildLayoutDelegate / SingleChildRenderObjectWidget?
Edit: In case there is no good answer out there, here's my SingleChildLayoutDelegate implementation. It is probably brittle.
import 'dart:math';
import 'package:flutter/material.dart';
class ConstrainedAspectRatio extends StatelessWidget {
const ConstrainedAspectRatio({required this.child, required this.maxAspectRatio, super.key});
final Widget child;
final double maxAspectRatio;
@override
Widget build(BuildContext context) => CustomSingleChildLayout(delegate: _CARDelegate(maxAspectRatio), child: child);
}
class _CARDelegate extends SingleChildLayoutDelegate {
_CARDelegate(this.maxAspectRatio);
final double maxAspectRatio;
Size _size = Size.zero;
@override
Size getSize(BoxConstraints constraints) {
// Full height, wide as allowed
final double w = constraints.maxWidth, h = constraints.maxHeight;
if (h.isInfinite) {
// Container infinitely tall; use max aspect ratio as exact aspect ratio
assert(w.isFinite, () => "Need at least one bounded constraint for $runtimeType.");
return _size = Size(w, w / maxAspectRatio);
} else {
// Finite height. Use all of it, and go as wide as maxAspectRatio allows.
return _size = Size(min(h * maxAspectRatio, w), h);
}
}
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints.tight(_size);
}
@override
bool shouldRelayout(covariant _CARDelegate oldDelegate) {
return oldDelegate.maxAspectRatio != maxAspectRatio;
}
}