Unverified Commit ee8488e8 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Remove warnings when `UnconstrainedBox` and `ConstraintsTransformBox` are clipped (#110393)

parent 45570b13
...@@ -656,9 +656,9 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox { ...@@ -656,9 +656,9 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
} }
} }
/// A [RenderBox] that applies an arbitrary transform to its [constraints] /// A [RenderBox] that applies an arbitrary transform to its constraints,
/// before sizing its child using the new constraints, treating any overflow as /// and sizes its child using the resulting [BoxConstraints], optionally
/// error. /// clipping, or treating the overflow as an error.
/// ///
/// This [RenderBox] sizes its child using a [BoxConstraints] created by /// This [RenderBox] sizes its child using a [BoxConstraints] created by
/// applying [constraintsTransform] to this [RenderBox]'s own [constraints]. /// applying [constraintsTransform] to this [RenderBox]'s own [constraints].
...@@ -668,9 +668,9 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox { ...@@ -668,9 +668,9 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
/// the entire child, the child will be clipped if [clipBehavior] is not /// the entire child, the child will be clipped if [clipBehavior] is not
/// [Clip.none]. /// [Clip.none].
/// ///
/// In debug mode, if the child overflows the box, a warning will be printed on /// In debug mode, if [clipBehavior] is [Clip.none] and the child overflows the
/// the console, and black and yellow striped areas will appear where the /// container, a warning will be printed on the console, and black and yellow
/// overflow occurs. /// striped areas will appear where the overflow occurs.
/// ///
/// When [child] is null, this [RenderBox] takes the smallest possible size and /// When [child] is null, this [RenderBox] takes the smallest possible size and
/// never overflows. /// never overflows.
...@@ -732,7 +732,9 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO ...@@ -732,7 +732,9 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
/// {@macro flutter.material.Material.clipBehavior} /// {@macro flutter.material.Material.clipBehavior}
/// ///
/// Defaults to [Clip.none], and must not be null. /// {@macro flutter.widgets.ConstraintsTransformBox.clipBehavior}
///
/// Defaults to [Clip.none].
Clip get clipBehavior => _clipBehavior; Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior; Clip _clipBehavior;
set clipBehavior(Clip value) { set clipBehavior(Clip value) {
...@@ -830,9 +832,17 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO ...@@ -830,9 +832,17 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
oldLayer: _clipRectLayer.layer, oldLayer: _clipRectLayer.layer,
); );
// Display the overflow indicator. // Display the overflow indicator if clipBehavior is Clip.none.
assert(() { assert(() {
switch (clipBehavior) {
case Clip.none:
paintOverflowIndicator(context, offset, _overflowContainerRect, _overflowChildRect); paintOverflowIndicator(context, offset, _overflowContainerRect, _overflowChildRect);
break;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
break;
}
return true; return true;
}()); }());
} }
......
...@@ -2513,8 +2513,8 @@ class ConstrainedBox extends SingleChildRenderObjectWidget { ...@@ -2513,8 +2513,8 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
} }
/// A container widget that applies an arbitrary transform to its constraints, /// A container widget that applies an arbitrary transform to its constraints,
/// and sizes its child using the resulting [BoxConstraints], treating any /// and sizes its child using the resulting [BoxConstraints], optionally
/// overflow as error. /// clipping, or treating the overflow as an error.
/// ///
/// This container sizes its child using a [BoxConstraints] created by applying /// This container sizes its child using a [BoxConstraints] created by applying
/// [constraintsTransform] to its own constraints. This container will then /// [constraintsTransform] to its own constraints. This container will then
...@@ -2523,12 +2523,12 @@ class ConstrainedBox extends SingleChildRenderObjectWidget { ...@@ -2523,12 +2523,12 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// [alignment]. If the container cannot expand enough to accommodate the entire /// [alignment]. If the container cannot expand enough to accommodate the entire
/// child, the child will be clipped if [clipBehavior] is not [Clip.none]. /// 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 /// In debug mode, if [clipBehavior] is [Clip.none] and the child overflows the
/// printed on the console, and black and yellow striped areas will appear where /// container, a warning will be printed on the console, and black and yellow
/// the overflow occurs. /// striped areas will appear where the overflow occurs.
/// ///
/// When [child] is null, this widget becomes as small as possible and never /// When [child] is null, this widget becomes as small as possible and never
/// overflows /// overflows.
/// ///
/// This widget can be used to ensure some of [child]'s natural dimensions are /// This widget can be used to ensure some of [child]'s natural dimensions are
/// honored, and get an early warning otherwise during development. For /// honored, and get an early warning otherwise during development. For
...@@ -2564,7 +2564,7 @@ class ConstrainedBox extends SingleChildRenderObjectWidget { ...@@ -2564,7 +2564,7 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// * [OverflowBox], a widget that imposes additional constraints on its child, /// * [OverflowBox], a widget that imposes additional constraints on its child,
/// and allows the child to overflow itself. /// and allows the child to overflow itself.
/// * [UnconstrainedBox] which allows its children to render themselves /// * [UnconstrainedBox] which allows its children to render themselves
/// unconstrained, expands to fit them, and considers overflow to be an error. /// unconstrained and expands to fit them.
class ConstraintsTransformBox extends SingleChildRenderObjectWidget { class ConstraintsTransformBox extends SingleChildRenderObjectWidget {
/// Creates a widget that uses a function to transform the constraints it /// 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 /// passes to its child. If the child overflows the parent's constraints, a
...@@ -2686,6 +2686,13 @@ class ConstraintsTransformBox extends SingleChildRenderObjectWidget { ...@@ -2686,6 +2686,13 @@ class ConstraintsTransformBox extends SingleChildRenderObjectWidget {
/// {@macro flutter.material.Material.clipBehavior} /// {@macro flutter.material.Material.clipBehavior}
/// ///
/// {@template flutter.widgets.ConstraintsTransformBox.clipBehavior}
/// In debug mode, if `clipBehavior` is [Clip.none], and the child overflows
/// its constraints, a warning will be printed on the console, and black and
/// yellow striped areas will appear where the overflow occurs. For other
/// values of `clipBehavior`, the contents are clipped accordingly.
/// {@endtemplate}
///
/// Defaults to [Clip.none]. /// Defaults to [Clip.none].
final Clip clipBehavior; final Clip clipBehavior;
......
...@@ -410,16 +410,65 @@ void main() { ...@@ -410,16 +410,65 @@ void main() {
expect(firstErrorDetails?.toString(), contains('is not normalized')); expect(firstErrorDetails?.toString(), contains('is not normalized'));
}); });
test('overflow is reported when insufficient size is given', () { test('overflow is reported when insufficient size is given and clipBehavior is Clip.none', () {
final RenderConstrainedBox child = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: double.maxFinite)); bool hadErrors = false;
final RenderConstraintsTransformBox box = RenderConstraintsTransformBox( void expectOverflowedErrors() {
absorbOverflowedErrors();
hadErrors = true;
}
final TestClipPaintingContext context = TestClipPaintingContext();
for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final RenderConstraintsTransformBox box;
switch (clip) {
case Clip.none:
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
box = RenderConstraintsTransformBox(
alignment: Alignment.center, alignment: Alignment.center,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity), constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity),
child: child, clipBehavior: clip!,
child: RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(
width: double.maxFinite,
height: double.maxFinite,
),
),
); );
break;
case null:
box = RenderConstraintsTransformBox(
alignment: Alignment.center,
textDirection: TextDirection.ltr,
constraintsTransform: (BoxConstraints constraints) => constraints.copyWith(maxWidth: double.infinity),
child: RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(
width: double.maxFinite,
height: double.maxFinite,
),
),
);
break;
}
layout(box, constraints: const BoxConstraints(), phase: EnginePhase.composite, onErrors: expectOverflowedErrors); layout(box, constraints: const BoxConstraints(), phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
context.paintChild(box, Offset.zero);
// By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none));
switch (clip) {
case null:
case Clip.none:
expect(hadErrors, isTrue, reason: 'Should have had overflow errors for $clip');
break;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
expect(hadErrors, isFalse, reason: 'Should not have had overflow errors for $clip');
break;
}
hadErrors = false;
}
}); });
test('handles flow layout', () { test('handles flow layout', () {
...@@ -642,26 +691,50 @@ void main() { ...@@ -642,26 +691,50 @@ void main() {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext(); final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none bool hadErrors = false;
final RenderUnconstrainedBox defaultBox = RenderUnconstrainedBox( void expectOverflowedErrors() {
absorbOverflowedErrors();
hadErrors = true;
}
for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final RenderUnconstrainedBox box;
switch (clip) {
case Clip.none:
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
box = RenderUnconstrainedBox(
alignment: Alignment.center, alignment: Alignment.center,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: box200x200, child: box200x200,
clipBehavior: clip!,
); );
layout(defaultBox, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); break;
context.paintChild(defaultBox, Offset.zero); case null:
expect(context.clipBehavior, equals(Clip.none)); box = RenderUnconstrainedBox(
for (final Clip clip in Clip.values) {
final RenderUnconstrainedBox box = RenderUnconstrainedBox(
alignment: Alignment.center, alignment: Alignment.center,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: box200x200, child: box200x200,
clipBehavior: clip,
); );
break;
}
layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
switch (clip) {
case null:
case Clip.none:
expect(hadErrors, isTrue, reason: 'Should have had overflow errors for $clip');
break;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
expect(hadErrors, isFalse, reason: 'Should not have had overflow errors for $clip');
break;
}
hadErrors = false;
context.paintChild(box, Offset.zero); context.paintChild(box, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none), reason: 'for $clip');
} }
}); });
......
...@@ -54,13 +54,18 @@ void main() { ...@@ -54,13 +54,18 @@ void main() {
TestRenderingFlutterBinding.ensureInitialized(); TestRenderingFlutterBinding.ensureInitialized();
test('RenderEditable respects clipBehavior', () { test('RenderEditable respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 1.0);
final TestClipPaintingContext context = TestClipPaintingContext();
final String longString = 'a' * 10000; final String longString = 'a' * 10000;
// By default, clipBehavior should be Clip.none for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final RenderEditable defaultEditable = RenderEditable( final TestClipPaintingContext context = TestClipPaintingContext();
final RenderEditable editable;
switch (clip) {
case Clip.none:
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
editable = RenderEditable(
text: TextSpan(text: longString), text: TextSpan(text: longString),
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
startHandleLayerLink: LayerLink(), startHandleLayerLink: LayerLink(),
...@@ -68,14 +73,11 @@ void main() { ...@@ -68,14 +73,11 @@ void main() {
offset: ViewportOffset.zero(), offset: ViewportOffset.zero(),
textSelectionDelegate: _FakeEditableTextState(), textSelectionDelegate: _FakeEditableTextState(),
selection: const TextSelection(baseOffset: 0, extentOffset: 0), selection: const TextSelection(baseOffset: 0, extentOffset: 0),
clipBehavior: clip!,
); );
layout(defaultEditable, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); break;
context.paintChild(defaultEditable, Offset.zero); case null:
expect(context.clipBehavior, equals(Clip.hardEdge)); editable = RenderEditable(
context.clipBehavior = Clip.none; // Reset as Clip.none won't write into clipBehavior.
for (final Clip clip in Clip.values) {
final RenderEditable editable = RenderEditable(
text: TextSpan(text: longString), text: TextSpan(text: longString),
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
startHandleLayerLink: LayerLink(), startHandleLayerLink: LayerLink(),
...@@ -83,11 +85,13 @@ void main() { ...@@ -83,11 +85,13 @@ void main() {
offset: ViewportOffset.zero(), offset: ViewportOffset.zero(),
textSelectionDelegate: _FakeEditableTextState(), textSelectionDelegate: _FakeEditableTextState(),
selection: const TextSelection(baseOffset: 0, extentOffset: 0), selection: const TextSelection(baseOffset: 0, extentOffset: 0),
clipBehavior: clip,
); );
layout(editable, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); break;
}
layout(editable, constraints: viewport, phase: EnginePhase.composite, onErrors: expectNoFlutterErrors);
context.paintChild(editable, Offset.zero); context.paintChild(editable, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior is Clip.hardEdge.
expect(context.clipBehavior, equals(clip ?? Clip.hardEdge), reason: 'for $clip');
} }
}); });
......
...@@ -60,18 +60,30 @@ void main() { ...@@ -60,18 +60,30 @@ void main() {
test('Clip behavior is respected', () { test('Clip behavior is respected', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext(); final TestClipPaintingContext context = TestClipPaintingContext();
bool hadErrors = false;
// By default, clipBehavior should be Clip.none
final RenderFlex defaultFlex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200]); for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
layout(defaultFlex, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); final RenderFlex flex;
context.paintChild(defaultFlex, Offset.zero); switch (clip) {
expect(context.clipBehavior, equals(Clip.none)); case Clip.none:
case Clip.hardEdge:
for (final Clip clip in Clip.values) { case Clip.antiAlias:
final RenderFlex flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200], clipBehavior: clip); case Clip.antiAliasWithSaveLayer:
layout(flex, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200], clipBehavior: clip!);
break;
case null:
flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200]);
break;
}
layout(flex, constraints: viewport, phase: EnginePhase.composite, onErrors: () {
absorbOverflowedErrors();
hadErrors = true;
});
context.paintChild(flex, Offset.zero); context.paintChild(flex, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none));
expect(hadErrors, isTrue);
hadErrors = false;
} }
}); });
......
...@@ -536,19 +536,24 @@ void main() { ...@@ -536,19 +536,24 @@ void main() {
test('RenderFittedBox respects clipBehavior', () { test('RenderFittedBox respects clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final TestClipPaintingContext context = TestClipPaintingContext(); final TestClipPaintingContext context = TestClipPaintingContext();
final RenderFittedBox box;
// By default, clipBehavior should be Clip.none switch (clip) {
final RenderFittedBox defaultBox = RenderFittedBox(child: box200x200, fit: BoxFit.none); case Clip.none:
layout(defaultBox, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); case Clip.hardEdge:
defaultBox.paint(context, Offset.zero); case Clip.antiAlias:
expect(context.clipBehavior, equals(Clip.none)); case Clip.antiAliasWithSaveLayer:
box = RenderFittedBox(child: box200x200, fit: BoxFit.none, clipBehavior: clip!);
for (final Clip clip in Clip.values) { break;
final RenderFittedBox box = RenderFittedBox(child: box200x200, fit: BoxFit.none, clipBehavior: clip); case null:
layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); box = RenderFittedBox(child: box200x200, fit: BoxFit.none);
break;
}
layout(box, constraints: viewport, phase: EnginePhase.composite, onErrors: expectNoFlutterErrors);
box.paint(context, Offset.zero); box.paint(context, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none));
} }
}); });
......
...@@ -382,13 +382,22 @@ class TestPushLayerPaintingContext extends PaintingContext { ...@@ -382,13 +382,22 @@ class TestPushLayerPaintingContext extends PaintingContext {
} }
} }
void expectOverflowedErrors() { // Absorbs errors that don't have "overflowed" in their error details.
final FlutterErrorDetails errorDetails = TestRenderingFlutterBinding.instance.takeFlutterErrorDetails()!; void absorbOverflowedErrors() {
final bool overflowed = errorDetails.toString().contains('overflowed'); final Iterable<FlutterErrorDetails> errorDetails = TestRenderingFlutterBinding.instance.takeAllFlutterErrorDetails();
if (!overflowed) { final Iterable<FlutterErrorDetails> filtered = errorDetails.where((FlutterErrorDetails details) {
FlutterError.reportError(errorDetails); return !details.toString().contains('overflowed');
});
if (filtered.isNotEmpty) {
filtered.forEach(FlutterError.reportError);
} }
} }
// Reports any FlutterErrors.
void expectNoFlutterErrors() {
final Iterable<FlutterErrorDetails> errorDetails = TestRenderingFlutterBinding.instance.takeAllFlutterErrorDetails();
errorDetails.forEach(FlutterError.reportError);
}
RenderConstrainedBox get box200x200 => RenderConstrainedBox get box200x200 =>
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0, width: 200.0)); RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0, width: 200.0));
...@@ -63,30 +63,39 @@ void main() { ...@@ -63,30 +63,39 @@ void main() {
expect(stack.size.height, equals(100.0)); expect(stack.size.height, equals(100.0));
}); });
test('Stack respects clipBehavior', () { test('Stack has correct clipBehavior', () {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none
final RenderStack defaultStack = RenderStack(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200]);
layout(defaultStack, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors);
defaultStack.paint(context, Offset.zero);
expect(context.clipBehavior, equals(Clip.none));
for (final Clip clip in Clip.values) { for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final TestClipPaintingContext context = TestClipPaintingContext();
final RenderBox child = box200x200; final RenderBox child = box200x200;
final RenderStack stack = RenderStack( final RenderStack stack;
switch(clip){
case Clip.none:
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
stack = RenderStack(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
children: <RenderBox>[child], children: <RenderBox>[child],
clipBehavior: clip, clipBehavior: clip!,
); );
break;
case null:
stack = RenderStack(
textDirection: TextDirection.ltr,
children: <RenderBox>[child],
);
break;
}
{ // Make sure that the child is positioned so the stack will consider it as overflowed. { // Make sure that the child is positioned so the stack will consider it as overflowed.
final StackParentData parentData = child.parentData! as StackParentData; final StackParentData parentData = child.parentData! as StackParentData;
parentData.left = parentData.right = 0; parentData.left = parentData.right = 0;
} }
layout(stack, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); layout(stack, constraints: viewport, phase: EnginePhase.composite, onErrors: expectNoFlutterErrors);
context.paintChild(stack, Offset.zero); context.paintChild(stack, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior should be Clip.hardEdge
expect(context.clipBehavior, equals(clip ?? Clip.hardEdge), reason: 'for $clip');
} }
}); });
......
...@@ -208,17 +208,23 @@ void main() { ...@@ -208,17 +208,23 @@ void main() {
const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final TestClipPaintingContext context = TestClipPaintingContext(); final TestClipPaintingContext context = TestClipPaintingContext();
// By default, clipBehavior should be Clip.none for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
final RenderWrap defaultWrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200]); final RenderWrap wrap;
layout(defaultWrap, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); switch(clip){
context.paintChild(defaultWrap, Offset.zero); case Clip.none:
expect(context.clipBehavior, equals(Clip.none)); case Clip.hardEdge:
case Clip.antiAlias:
for (final Clip clip in Clip.values) { case Clip.antiAliasWithSaveLayer:
final RenderWrap wrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200], clipBehavior: clip); wrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200], clipBehavior: clip!);
layout(wrap, constraints: viewport, phase: EnginePhase.composite, onErrors: expectOverflowedErrors); break;
case null:
wrap = RenderWrap(textDirection: TextDirection.ltr, children: <RenderBox>[box200x200]);
break;
}
layout(wrap, constraints: viewport, phase: EnginePhase.composite, onErrors: expectNoFlutterErrors);
context.paintChild(wrap, Offset.zero); context.paintChild(wrap, Offset.zero);
expect(context.clipBehavior, equals(clip)); // By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none));
} }
}); });
} }
...@@ -578,6 +578,55 @@ void main() { ...@@ -578,6 +578,55 @@ void main() {
expect(renderObject.clipBehavior, equals(Clip.antiAlias)); expect(renderObject.clipBehavior, equals(Clip.antiAlias));
}); });
testWidgets('UnconstrainedBox warns only when clipBehavior is Clip.none', (WidgetTester tester) async {
for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
// Clear any render objects that were there before so that we can see more
// than one error. Otherwise, it just throws the first one and skips the
// rest, since the render objects haven't changed.
await tester.pumpWidget(const SizedBox());
await tester.pumpWidget(
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200, maxWidth: 200),
child: clip == null
? const UnconstrainedBox(child: SizedBox(width: 400, height: 400))
: UnconstrainedBox(
clipBehavior: clip,
child: const SizedBox(width: 400, height: 400),
),
),
),
);
final RenderConstraintsTransformBox renderObject = tester.allRenderObjects.whereType<RenderConstraintsTransformBox>().first;
// Defaults to Clip.none
expect(renderObject.clipBehavior, equals(clip ?? Clip.none), reason: 'for clip = $clip');
switch(clip) {
case null:
case Clip.none:
// the UnconstrainedBox overflows.
final dynamic exception = tester.takeException();
expect(exception, isFlutterError, reason: 'for clip = $clip');
// ignore: avoid_dynamic_calls
expect(exception.diagnostics.first.level, DiagnosticLevel.summary, reason: 'for clip = $clip');
expect(
// ignore: avoid_dynamic_calls
exception.diagnostics.first.toString(),
startsWith('A RenderConstraintsTransformBox overflowed'),
reason: 'for clip = $clip',
);
break;
case Clip.hardEdge:
case Clip.antiAlias:
case Clip.antiAliasWithSaveLayer:
expect(tester.takeException(), isNull, reason: 'for clip = $clip');
break;
}
}
});
group('ConstraintsTransformBox', () { group('ConstraintsTransformBox', () {
test('toString', () { test('toString', () {
expect( expect(
......
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