Is there a Flutter widget specifying a min or max aspect ratio, rather than an exact ratio?

Viewed 22

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;
  }
}
0 Answers
Related