Unverified Commit 8c44abc0 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

ConstraintsTransformBox (#77994)

parent 74a795d1
......@@ -85,7 +85,8 @@ class _OverflowRegionData {
///
/// See also:
///
/// * [RenderUnconstrainedBox] and [RenderFlex] for examples of classes that use this indicator mixin.
/// * [RenderConstraintsTransformBox], [RenderUnconstrainedBox] and
/// [RenderFlex], for examples of classes that use this indicator mixin.
mixin DebugOverflowIndicatorMixin on RenderObject {
static const Color _black = Color(0xBF000000);
static const Color _yellow = Color(0xBFFFFF00);
......
......@@ -10,10 +10,17 @@ import 'box.dart';
import 'debug.dart';
import 'debug_overflow_indicator.dart';
import 'layer.dart';
import 'layout_helper.dart';
import 'object.dart';
import 'stack.dart' show RelativeRect;
/// Signature for a function that transforms a [BoxConstraints] to another
/// [BoxConstraints].
///
/// Used by [RenderConstraintsTransformBox] and [ConstraintsTransformBox].
/// Typically the caller requires the returned [BoxConstraints] to be
/// [BoxConstraints.isNormalized].
typedef BoxConstraintsTransform = BoxConstraints Function(BoxConstraints);
/// Abstract class for one-child-layout render boxes that provide control over
/// the child's position.
abstract class RenderShiftedBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
......@@ -628,71 +635,83 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
}
}
/// Renders a box, imposing no constraints on its child, allowing the child to
/// render at its "natural" size.
/// A [RenderBox] that applies an arbitrary transform to its [constraints]
/// before sizing its child using the new constraints, treating any overflow as
/// error.
///
/// This allows a child to render at the size it would render if it were alone
/// on an infinite canvas with no constraints. This box will then attempt to
/// adopt the same size, within the limits of its own constraints. If it ends
/// up with a different size, it will align the child based on [alignment].
/// If the box cannot expand enough to accommodate the entire child, the
/// child will be clipped.
/// This [RenderBox] sizes its child using a [BoxConstraints] created by
/// applying [constraintsTransform] to this [RenderBox]'s own [constraints].
/// This box will then attempt to adopt the same size, within the limits of its
/// own constraints. If it ends up with a different size, it will align the
/// child based on [alignment]. If the box cannot expand enough to accommodate
/// the entire child, the child will be clipped if [clipBehavior] is not
/// [Clip.none].
///
/// In debug mode, if the child overflows the box, a warning will be printed on
/// the console, and black and yellow striped areas will appear where the
/// overflow occurs.
///
/// When [child] is null, this [RenderBox] takes the smallest possible size and
/// never overflows.
///
/// This [RenderBox] can be used to ensure some of [child]'s natrual dimensions
/// are honored, and get an early warning during development otherwise. For
/// instance, if [child] requires a minimum height to fully display its content,
/// [constraintsTransform] can be set to a function that removes the `maxHeight`
/// constraint from the incoming [BoxConstraints], so that if the parent
/// [RenderObject] fails to provide enough vertical space, a warning will be
/// displayed in debug mode, while still allowing [child] to grow vertically.
///
/// See also:
///
/// * [ConstraintsTransformBox], the widget that makes use of this
/// [RenderObject] and exposes the same functionality.
/// * [RenderConstrainedBox], which renders a box which imposes constraints
/// on its child.
/// * [RenderConstrainedOverflowBox], which renders a box that imposes different
/// constraints on its child than it gets from its parent, possibly allowing
/// the child to overflow the parent.
/// * [RenderSizedOverflowBox], a render object that is a specific size but
/// passes its original constraints through to its child, which it allows to
/// overflow.
class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflowIndicatorMixin {
/// Create a render object that sizes itself to the child but does not
/// pass the [constraints] down to that child.
/// * [RenderUnconstrainedBox] which allows its children to render themselves
/// unconstrained, expands to fit them, and considers overflow to be an error.
class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugOverflowIndicatorMixin {
/// Creates a [RenderBox] that sizes itself to the child and modifies the
/// [constraints] before passing it down to that child.
///
/// The [alignment] must not be null.
RenderUnconstrainedBox({
/// The [alignment] and [clipBehavior] must not be null.
RenderConstraintsTransformBox({
required AlignmentGeometry alignment,
required TextDirection? textDirection,
Axis? constrainedAxis,
required BoxConstraintsTransform constraintsTransform,
RenderBox? child,
Clip clipBehavior = Clip.none,
}) : assert(alignment != null),
assert(clipBehavior != null),
_constrainedAxis = constrainedAxis,
assert(constraintsTransform != null),
_constraintsTransform = constraintsTransform,
_clipBehavior = clipBehavior,
super.mixin(alignment, textDirection, child);
/// The axis to retain constraints on, if any.
///
/// If not set, or set to null (the default), neither axis will retain its
/// constraints. If set to [Axis.vertical], then vertical constraints will
/// be retained, and if set to [Axis.horizontal], then horizontal constraints
/// will be retained.
Axis? get constrainedAxis => _constrainedAxis;
Axis? _constrainedAxis;
set constrainedAxis(Axis? value) {
if (_constrainedAxis == value)
/// {@macro flutter.widgets.constraintsTransform}
BoxConstraintsTransform get constraintsTransform => _constraintsTransform;
BoxConstraintsTransform _constraintsTransform;
set constraintsTransform(BoxConstraintsTransform value) {
if (_constraintsTransform == value)
return;
_constrainedAxis = value;
markNeedsLayout();
_constraintsTransform = value;
// The RenderObject only needs layout if the new transform maps the current
// `constraints` to a different value, or the render object has never been
// laid out before.
final bool needsLayout = _childConstraints == null
|| _childConstraints != value(constraints);
if (needsLayout)
markNeedsLayout();
}
Rect _overflowContainerRect = Rect.zero;
Rect _overflowChildRect = Rect.zero;
bool _isOverflowing = false;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.none], and must not be null.
Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior = Clip.none;
Clip _clipBehavior;
set clipBehavior(Clip value) {
assert(value != null);
if (value != _clipBehavior) {
......@@ -702,49 +721,33 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
}
}
Size _calculateSizeWithChild({required BoxConstraints constraints, required ChildLayouter layoutChild}) {
assert(child != null);
// Let the child lay itself out at it's "natural" size, but if
// constrainedAxis is non-null, keep any constraints on that axis.
final BoxConstraints childConstraints;
if (constrainedAxis != null) {
switch (constrainedAxis!) {
case Axis.horizontal:
childConstraints = BoxConstraints(maxWidth: constraints.maxWidth, minWidth: constraints.minWidth);
break;
case Axis.vertical:
childConstraints = BoxConstraints(maxHeight: constraints.maxHeight, minHeight: constraints.minHeight);
break;
}
} else {
childConstraints = const BoxConstraints();
}
return constraints.constrain(layoutChild(child!, childConstraints));
}
@override
Size computeDryLayout(BoxConstraints constraints) {
if (child == null) {
return constraints.smallest;
}
return _calculateSizeWithChild(
constraints: constraints,
layoutChild: ChildLayoutHelper.dryLayoutChild,
);
final Size? childSize = child?.getDryLayout(constraintsTransform(constraints));
return childSize == null ? constraints.smallest : constraints.constrain(childSize);
}
Rect _overflowContainerRect = Rect.zero;
Rect _overflowChildRect = Rect.zero;
bool _isOverflowing = false;
BoxConstraints? _childConstraints;
@override
void performLayout() {
final BoxConstraints constraints = this.constraints;
final RenderBox? child = this.child;
if (child != null) {
size = _calculateSizeWithChild(
constraints: constraints,
layoutChild: ChildLayoutHelper.layoutChild,
);
final BoxConstraints childConstraints = constraintsTransform(constraints);
assert(childConstraints != null);
assert(childConstraints.isNormalized, '$childConstraints is not normalized');
_childConstraints = childConstraints;
child.layout(childConstraints, parentUsesSize: true);
size = constraints.constrain(child.size);
alignChild();
final BoxParentData childParentData = child!.parentData! as BoxParentData;
final BoxParentData childParentData = child.parentData! as BoxParentData;
_overflowContainerRect = Offset.zero & size;
_overflowChildRect = childParentData.offset & child!.size;
_overflowChildRect = childParentData.offset & child.size;
} else {
size = constraints.smallest;
_overflowContainerRect = Rect.zero;
......@@ -797,6 +800,94 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
}
}
/// Renders a box, imposing no constraints on its child, allowing the child to
/// render at its "natural" size.
///
/// The class is deprecated, use [RenderConstraintsTransformBox] instead.
///
/// This allows a child to render at the size it would render if it were alone
/// on an infinite canvas with no constraints. This box will then attempt to
/// adopt the same size, within the limits of its own constraints. If it ends
/// up with a different size, it will align the child based on [alignment].
/// If the box cannot expand enough to accommodate the entire child, the
/// child will be clipped.
///
/// In debug mode, if the child overflows the box, a warning will be printed on
/// the console, and black and yellow striped areas will appear where the
/// overflow occurs.
///
/// See also:
///
/// * [RenderConstrainedBox], which renders a box which imposes constraints
/// on its child.
/// * [RenderConstrainedOverflowBox], which renders a box that imposes different
/// constraints on its child than it gets from its parent, possibly allowing
/// the child to overflow the parent.
/// * [RenderSizedOverflowBox], a render object that is a specific size but
/// passes its original constraints through to its child, which it allows to
/// overflow.
///
@Deprecated(
'Use RenderConstraintsTransformBox instead. '
'This feature was deprecated after v2.1.0-11.0.pre.'
)
class RenderUnconstrainedBox extends RenderConstraintsTransformBox {
/// Create a render object that sizes itself to the child but does not
/// pass the [constraints] down to that child.
///
/// The [alignment] and [clipBehavior] must not be null.
@Deprecated(
'Use RenderConstraintsTransformBox instead. '
'This feature was deprecated after v2.1.0-11.0.pre.'
)
RenderUnconstrainedBox({
required AlignmentGeometry alignment,
required TextDirection? textDirection,
Axis? constrainedAxis,
RenderBox? child,
Clip clipBehavior = Clip.none,
}) : assert(alignment != null),
assert(clipBehavior != null),
_constrainedAxis = constrainedAxis,
super(
alignment: alignment,
textDirection: textDirection,
child: child,
clipBehavior: clipBehavior,
constraintsTransform: _convertAxis(constrainedAxis),
);
/// The axis to retain constraints on, if any.
///
/// If not set, or set to null (the default), neither axis will retain its
/// constraints. If set to [Axis.vertical], then vertical constraints will
/// be retained, and if set to [Axis.horizontal], then horizontal constraints
/// will be retained.
Axis? get constrainedAxis => _constrainedAxis;
Axis? _constrainedAxis;
set constrainedAxis(Axis? value) {
if (_constrainedAxis == value)
return;
_constrainedAxis = value;
constraintsTransform = _convertAxis(constrainedAxis);
}
static BoxConstraints _unconstrained(BoxConstraints constraints) => const BoxConstraints();
static BoxConstraints _widthConstrained(BoxConstraints constraints) => constraints.widthConstraints();
static BoxConstraints _heightConstrained(BoxConstraints constraints) => constraints.heightConstraints();
static BoxConstraintsTransform _convertAxis(Axis? constrainedAxis) {
if (constrainedAxis == null) {
return _unconstrained;
}
switch (constrainedAxis) {
case Axis.horizontal:
return _widthConstrained;
case Axis.vertical:
return _heightConstrained;
}
}
}
/// A render object that is a specific size but passes its original constraints
/// through to its child, which it allows to overflow.
///
......
......@@ -28,6 +28,7 @@ export 'package:flutter/rendering.dart' show
AlignmentGeometryTween,
Axis,
BoxConstraints,
BoxConstraintsTransform,
CrossAxisAlignment,
CustomClipper,
CustomPainter,
......@@ -2317,6 +2318,221 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
}
}
/// A container widget that applies an arbitrary transform to its constraints,
/// and sizes its child using the resulting [BoxConstraints], treating any
/// overflow as error.
///
/// This container sizes its child using a [BoxConstraints] created by applying
/// [constraintsTransform] to its own constraints. This container will then
/// attempt to adopt the same size, within the limits of its own constraints. If
/// it ends up with a different size, it will align the child based on
/// [alignment]. If the container cannot expand enough to accommodate the entire
/// child, the child will be clipped if [clipBehavior] is not [Clip.none].
///
/// In debug mode, if the child overflows the container, a warning will be
/// printed on the console, and black and yellow striped areas will appear where
/// the overflow occurs.
///
/// When [child] is null, this widget becomes as small as possible and never
/// overflows
///
/// This widget can be used to ensure some of [child]'s natrual dimensions are
/// honored, and get an early warning otherwise during development. For
/// instance, if [child] requires a minimum height to fully display its content,
/// [constraintsTransform] can be set to [maxHeightUnconstrained], so that if
/// the parent [RenderObject] fails to provide enough vertical space, a warning
/// will be displayed in debug mode, while still allowing [child] to grow
/// vertically:
///
/// {@tool snippet}
/// In the following snippet, the [Card] is guaranteed to be at least as tall as
/// its "natrual" height. Unlike [UnconstrainedBox], it will become taller if
/// its "natrual" height is smaller than 40 px. If the [Container] isn't high
/// enough to show the full content of the [Card], in debug mode a warning will
/// be given.
///
/// ```dart
/// Container(
/// constraints: BoxConstraints(minHeight: 40, maxHeight: 100),
/// alignment: Alignment.center,
/// child: ConstraintsTransformBox(
/// constraintsTransform: ConstraintsTransformBox.maxHeightUnconstrained,
/// child: const Card(child: Text('Hello World!')),
/// )
/// )
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [ConstrainedBox], which renders a box which imposes constraints
/// on its child.
/// * [OverflowBox], a widget that imposes additional constraints on its child,
/// and allows the child to overflow itself.
/// * [UnconstrainedBox] which allows its children to render themselves
/// unconstrained, expands to fit them, and considers overflow to be an error.
class ConstraintsTransformBox extends SingleChildRenderObjectWidget {
/// Creates a widget that uses a function to transform the constraints it
/// passes to its child. If the child overflows the parent's constraints, a
/// warning will be given in debug mode.
///
/// The `debugTransformType` argument adds a debug label to this widget.
///
/// The `alignment`, `clipBehavior` and `constraintsTransform` arguments must
/// not be null.
const ConstraintsTransformBox({
Key? key,
Widget? child,
this.textDirection,
this.alignment = Alignment.center,
required this.constraintsTransform,
this.clipBehavior = Clip.none,
String debugTransformType = '',
}) : _debugTransformLabel = debugTransformType,
assert(alignment != null),
assert(clipBehavior != null),
assert(constraintsTransform != null),
assert(debugTransformType != null),
super(key: key, child: child);
/// A [BoxConstraintsTransform] that always returns its argument as-is (i.e.,
/// it is an identity function).
///
/// The [ConstraintsTransformBox] becomes a proxy widget that has no effect on
/// layout if [constraintsTransform] is set to this.
static BoxConstraints unmodified(BoxConstraints constraints) => constraints;
/// A [BoxConstraintsTransform] that always returns a [BoxConstraints] that
/// imposes no constraints on either dimension (i.e. `const BoxConstraints()`).
///
/// Setting [constraintsTransform] to this allows [child] to render at its
/// "natural" size (equivalent to an [UnconstrainedBox] with `constrainedAxis`
/// set to null).
static BoxConstraints unconstrained(BoxConstraints constraints) => const BoxConstraints();
/// A [BoxConstraintsTransform] that removes the width constraints from the
/// input.
///
/// Setting [constraintsTransform] to this allows [child] to render at its
/// "natural" width (equivalent to an [UnconstrainedBox] with
/// `constrainedAxis` set to [Axis.horizontal]).
static BoxConstraints widthUnconstrained(BoxConstraints constraints) => constraints.heightConstraints();
/// A [BoxConstraintsTransform] that removes the height constraints from the
/// input.
///
/// Setting [constraintsTransform] to this allows [child] to render at its
/// "natural" height (equivalent to an [UnconstrainedBox] with
/// `constrainedAxis` set to [Axis.vertical]).
static BoxConstraints heightUnconstrained(BoxConstraints constraints) => constraints.widthConstraints();
/// A [BoxConstraintsTransform] that removes the `maxHeight` constraint from
/// the input.
///
/// Setting [constraintsTransform] to this allows [child] to render at its
/// "natural" height or the `minHeight` of the incoming [BoxConstraints],
/// whichever is larger.
static BoxConstraints maxHeightUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxHeight: double.infinity);
/// A [BoxConstraintsTransform] that removes the `maxWidth` constraint from
/// the input.
///
/// Setting [constraintsTransform] to this allows [child] to render at its
/// "natural" width or the `minWidth` of the incoming [BoxConstraints],
/// whichever is larger.
static BoxConstraints maxWidthUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity);
/// A [BoxConstraintsTransform] that removes both the `maxWidth` and the
/// `maxHeight` constraints from the input.
///
/// Setting [constraintsTransform] to this allows [child] to render at least
/// its "natural" size, and grow along an axis if the incoming
/// [BoxConstraints] has a larger minimum constraint on that axis.
static BoxConstraints maxUnconstrained(BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity, maxHeight: double.infinity);
static final Map<BoxConstraintsTransform, String> _debugKnownTransforms = <BoxConstraintsTransform, String>{
unmodified: 'unmodified',
unconstrained: 'unconstrained',
widthUnconstrained: 'width constraints removed',
heightUnconstrained: 'height constraints removed',
maxWidthUnconstrained: 'maxWidth constraint removed',
maxHeightUnconstrained: 'maxHeight constraint removed',
maxUnconstrained: 'maxWidth & maxHeight constraints removed',
};
/// The text direction to use when interpreting the [alignment] if it is an
/// [AlignmentDirectional].
///
/// Defaults to null, in which case [Directionality.maybeOf] is used to determine
/// the text direction.
final TextDirection? textDirection;
/// The alignment to use when laying out the child, if it has a different size
/// than this widget.
///
/// If this is an [AlignmentDirectional], then [textDirection] must not be
/// null.
///
/// See also:
///
/// * [Alignment] for non-[Directionality]-aware alignments.
/// * [AlignmentDirectional] for [Directionality]-aware alignments.
final AlignmentGeometry alignment;
/// {@template flutter.widgets.constraintsTransform}
/// The function used to transform the incoming [BoxConstraints], to size
/// [child].
///
/// The function must return a [BoxConstraints] that is
/// [BoxConstraints.isNormalized].
///
/// See [ConstraintsTransformBox] for predefined common
/// [BoxConstraintsTransform]s.
/// {@endtemplate}
final BoxConstraintsTransform constraintsTransform;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.none].
final Clip clipBehavior;
final String _debugTransformLabel;
@override
RenderConstraintsTransformBox createRenderObject(BuildContext context) {
return RenderConstraintsTransformBox(
textDirection: textDirection ?? Directionality.maybeOf(context),
alignment: alignment,
constraintsTransform: constraintsTransform,
clipBehavior: clipBehavior,
);
}
@override
void updateRenderObject(BuildContext context, covariant RenderConstraintsTransformBox renderObject) {
renderObject
..textDirection = textDirection ?? Directionality.maybeOf(context)
..constraintsTransform = constraintsTransform
..alignment = alignment
..clipBehavior = clipBehavior;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
final String? debugTransformLabel = _debugTransformLabel.isNotEmpty
? _debugTransformLabel
: _debugKnownTransforms[constraintsTransform];
if (debugTransformLabel != null) {
properties.add(DiagnosticsProperty<String>('constraints transform', debugTransformLabel));
}
}
}
/// A widget that imposes no constraints on its child, allowing it to render
/// at its "natural" size.
///
......@@ -2341,20 +2557,23 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// * [OverflowBox], a widget that imposes different constraints on its child
/// than it gets from its parent, possibly allowing the child to overflow
/// the parent.
class UnconstrainedBox extends SingleChildRenderObjectWidget {
/// * [ConstraintsTransformBox], a widget that sizes its child using a
/// transformed [BoxConstraints], and shows a warning if the child overflows
/// in debug mode.
class UnconstrainedBox extends StatelessWidget {
/// Creates a widget that imposes no constraints on its child, allowing it to
/// render at its "natural" size. If the child overflows the parents
/// constraints, a warning will be given in debug mode.
const UnconstrainedBox({
Key? key,
Widget? child,
this.child,
this.textDirection,
this.alignment = Alignment.center,
this.constrainedAxis,
this.clipBehavior = Clip.none,
}) : assert(alignment != null),
assert(clipBehavior != null),
super(key: key, child: child);
super(key: key);
/// The text direction to use when interpreting the [alignment] if it is an
/// [AlignmentDirectional].
......@@ -2384,22 +2603,34 @@ class UnconstrainedBox extends SingleChildRenderObjectWidget {
/// Defaults to [Clip.none].
final Clip clipBehavior;
@override
void updateRenderObject(BuildContext context, covariant RenderUnconstrainedBox renderObject) {
renderObject
..textDirection = textDirection ?? Directionality.maybeOf(context)
..alignment = alignment
..constrainedAxis = constrainedAxis
..clipBehavior = clipBehavior;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final Widget? child;
BoxConstraintsTransform _axisToTransform(Axis? constrainedAxis) {
if (constrainedAxis != null) {
switch (constrainedAxis) {
case Axis.horizontal:
return ConstraintsTransformBox.heightUnconstrained;
case Axis.vertical:
return ConstraintsTransformBox.widthUnconstrained;
}
} else {
return ConstraintsTransformBox.unconstrained;
}
}
@override
RenderUnconstrainedBox createRenderObject(BuildContext context) => RenderUnconstrainedBox(
textDirection: textDirection ?? Directionality.maybeOf(context),
alignment: alignment,
constrainedAxis: constrainedAxis,
clipBehavior: clipBehavior,
);
Widget build(BuildContext context) {
return ConstraintsTransformBox(
child: child,
textDirection: textDirection,
alignment: alignment,
clipBehavior: clipBehavior,
constraintsTransform: _axisToTransform(constrainedAxis),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
......
......@@ -378,6 +378,48 @@ void main() {
expect(unconstrained.getMaxIntrinsicWidth(100.0), equals(200.0));
});
group('ConstraintsTransfromBox', () {
FlutterErrorDetails? firstErrorDetails;
void exhaustErrors() {
FlutterErrorDetails? next;
do {
next = renderer.takeFlutterErrorDetails();
firstErrorDetails ??= next;
} while (next != null);
}
tearDown(() {
firstErrorDetails = null;
});
test('throws if the resulting constraints are not normalized', () {
final RenderConstrainedBox child = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 0));
final RenderConstraintsTransformBox box = RenderConstraintsTransformBox(
alignment: Alignment.center,
textDirection: TextDirection.ltr,
constraintsTransform: (BoxConstraints constraints) => const BoxConstraints(maxHeight: -1, minHeight: 200),
child: child,
);
layout(box, constraints: const BoxConstraints(), onErrors: exhaustErrors);
expect(firstErrorDetails?.toString(), contains('is not normalized'));
});
test('overflow is reported when insufficient size is given', () {
final RenderConstrainedBox child = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: double.maxFinite));
final RenderConstraintsTransformBox box = RenderConstraintsTransformBox(
alignment: Alignment.center,
textDirection: TextDirection.ltr,
constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity),
child: child,
);
// No error reported.
layout(box, constraints: const BoxConstraints(), phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
});
});
test ('getMinIntrinsicWidth error handling', () {
final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox(
textDirection: TextDirection.ltr,
......
......@@ -36,7 +36,7 @@ void main() {
final dynamic exception = tester.takeException();
expect(exception, isFlutterError);
expect(exception.diagnostics.first.level, DiagnosticLevel.summary);
expect(exception.diagnostics.first.toString(), startsWith('A RenderUnconstrainedBox overflowed by '));
expect(exception.diagnostics.first.toString(), startsWith('A RenderConstraintsTransformBox overflowed by '));
expect(find.byType(UnconstrainedBox), paints..rect());
await tester.pumpWidget(
......
......@@ -323,6 +323,7 @@ void main() {
const UnconstrainedBox(constrainedAxis: Axis.vertical,).toString(),
equals('UnconstrainedBox(alignment: Alignment.center, constrainedAxis: vertical)'),
);
expect(
const UnconstrainedBox(constrainedAxis: Axis.horizontal, textDirection: TextDirection.rtl, alignment: Alignment.topRight).toString(),
equals('UnconstrainedBox(alignment: Alignment.topRight, constrainedAxis: horizontal, textDirection: rtl)'),
......@@ -331,13 +332,32 @@ void main() {
testWidgets('UnconstrainedBox can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(const UnconstrainedBox());
final RenderUnconstrainedBox renderObject = tester.allRenderObjects.whereType<RenderUnconstrainedBox>().first;
final RenderConstraintsTransformBox renderObject = tester.allRenderObjects.whereType<RenderConstraintsTransformBox>().first;
expect(renderObject.clipBehavior, equals(Clip.none));
await tester.pumpWidget(const UnconstrainedBox(clipBehavior: Clip.antiAlias));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
group('ConstraintsTransformBox', () {
test('toString', () {
expect(
const ConstraintsTransformBox(
constraintsTransform: ConstraintsTransformBox.unconstrained,
).toString(),
equals('ConstraintsTransformBox(alignment: Alignment.center, constraints transform: unconstrained)'),
);
expect(
const ConstraintsTransformBox(
textDirection: TextDirection.rtl,
alignment: Alignment.topRight,
constraintsTransform: ConstraintsTransformBox.widthUnconstrained,
).toString(),
equals('ConstraintsTransformBox(alignment: Alignment.topRight, textDirection: rtl, constraints transform: width constraints removed)'),
);
});
});
group('ColoredBox', () {
late _MockCanvas mockCanvas;
late _MockPaintingContext mockContext;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment