FittedBox RTL (#12662)
This commit is contained in:
@@ -1764,15 +1764,30 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
///
|
||||
/// The [fit] and [alignment] arguments must not be null.
|
||||
RenderFittedBox({
|
||||
RenderBox child,
|
||||
BoxFit fit: BoxFit.contain,
|
||||
Alignment alignment: Alignment.center
|
||||
AlignmentGeometry alignment: Alignment.center,
|
||||
TextDirection textDirection,
|
||||
RenderBox child,
|
||||
}) : assert(fit != null),
|
||||
assert(alignment != null),
|
||||
_fit = fit,
|
||||
_alignment = alignment,
|
||||
_textDirection = textDirection,
|
||||
super(child);
|
||||
|
||||
Alignment _resolvedAlignment;
|
||||
|
||||
void _resolve() {
|
||||
if (_resolvedAlignment != null)
|
||||
return;
|
||||
_resolvedAlignment = alignment.resolve(textDirection);
|
||||
}
|
||||
|
||||
void _markNeedResolution() {
|
||||
_resolvedAlignment = null;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
/// How to inscribe the child into the space allocated during layout.
|
||||
BoxFit get fit => _fit;
|
||||
BoxFit _fit;
|
||||
@@ -1790,17 +1805,36 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
/// An alignment of (0.0, 0.0) aligns the child to the top-left corner of its
|
||||
/// parent's bounds. An alignment of (1.0, 0.5) aligns the child to the middle
|
||||
/// of the right edge of its parent's bounds.
|
||||
Alignment get alignment => _alignment;
|
||||
Alignment _alignment;
|
||||
set alignment(Alignment value) {
|
||||
assert(value != null && value.x != null && value.y != null);
|
||||
///
|
||||
/// If this is set to a [AlignmentDirectional] object, then
|
||||
/// [textDirection] must not be null.
|
||||
AlignmentGeometry get alignment => _alignment;
|
||||
AlignmentGeometry _alignment;
|
||||
set alignment(AlignmentGeometry value) {
|
||||
assert(value != null);
|
||||
if (_alignment == value)
|
||||
return;
|
||||
_alignment = value;
|
||||
_clearPaintData();
|
||||
markNeedsPaint();
|
||||
_markNeedResolution();
|
||||
}
|
||||
|
||||
/// The text direction with which to resolve [alignment].
|
||||
///
|
||||
/// This may be changed to null, but only after [alignment] has been changed
|
||||
/// to a value that does not depend on the direction.
|
||||
TextDirection get textDirection => _textDirection;
|
||||
TextDirection _textDirection;
|
||||
set textDirection(TextDirection value) {
|
||||
if (_textDirection == value)
|
||||
return;
|
||||
_textDirection = value;
|
||||
_clearPaintData();
|
||||
_markNeedResolution();
|
||||
}
|
||||
|
||||
// TODO(ianh): The intrinsic dimensions of this box are wrong.
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
if (child != null) {
|
||||
@@ -1828,12 +1862,13 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
_hasVisualOverflow = false;
|
||||
_transform = new Matrix4.identity();
|
||||
} else {
|
||||
_resolve();
|
||||
final Size childSize = child.size;
|
||||
final FittedSizes sizes = applyBoxFit(_fit, childSize, size);
|
||||
final double scaleX = sizes.destination.width / sizes.source.width;
|
||||
final double scaleY = sizes.destination.height / sizes.source.height;
|
||||
final Rect sourceRect = _alignment.inscribe(sizes.source, Offset.zero & childSize);
|
||||
final Rect destinationRect = _alignment.inscribe(sizes.destination, Offset.zero & size);
|
||||
final Rect sourceRect = _resolvedAlignment.inscribe(sizes.source, Offset.zero & childSize);
|
||||
final Rect destinationRect = _resolvedAlignment.inscribe(sizes.destination, Offset.zero & size);
|
||||
_hasVisualOverflow = sourceRect.width < childSize.width || sourceRect.height < childSize.width;
|
||||
_transform = new Matrix4.translationValues(destinationRect.left, destinationRect.top, 0.0)
|
||||
..scale(scaleX, scaleY, 1.0)
|
||||
@@ -1894,6 +1929,7 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
super.debugFillProperties(description);
|
||||
description.add(new EnumProperty<BoxFit>('fit', fit));
|
||||
description.add(new DiagnosticsProperty<Alignment>('alignment', alignment));
|
||||
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
|
||||
/// The [alignment] argument must not be null.
|
||||
RenderAligningShiftedBox({
|
||||
AlignmentGeometry alignment: Alignment.center,
|
||||
TextDirection textDirection,
|
||||
@required TextDirection textDirection,
|
||||
RenderBox child,
|
||||
}) : assert(alignment != null),
|
||||
_alignment = alignment,
|
||||
@@ -265,7 +265,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
|
||||
AlignmentGeometry _alignment;
|
||||
/// Sets the alignment to a new value, and triggers a layout update.
|
||||
///
|
||||
/// The new alignment must not be null or have any null properties.
|
||||
/// The new alignment must not be null.
|
||||
set alignment(AlignmentGeometry value) {
|
||||
assert(value != null);
|
||||
if (_alignment == value)
|
||||
|
||||
@@ -961,7 +961,7 @@ class FittedBox extends SingleChildRenderObjectWidget {
|
||||
Key key,
|
||||
this.fit: BoxFit.contain,
|
||||
this.alignment: Alignment.center,
|
||||
Widget child
|
||||
Widget child,
|
||||
}) : assert(fit != null),
|
||||
assert(alignment != null),
|
||||
super(key: key, child: child);
|
||||
@@ -974,16 +974,30 @@ class FittedBox extends SingleChildRenderObjectWidget {
|
||||
/// An alignment of (-1.0, -1.0) aligns the child to the top-left corner of its
|
||||
/// parent's bounds. An alignment of (1.0, 0.0) aligns the child to the middle
|
||||
/// of the right edge of its parent's bounds.
|
||||
final Alignment alignment;
|
||||
final AlignmentGeometry alignment;
|
||||
|
||||
@override
|
||||
RenderFittedBox createRenderObject(BuildContext context) => new RenderFittedBox(fit: fit, alignment: alignment);
|
||||
RenderFittedBox createRenderObject(BuildContext context) {
|
||||
return new RenderFittedBox(
|
||||
fit: fit,
|
||||
alignment: alignment,
|
||||
textDirection: Directionality.of(context),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderFittedBox renderObject) {
|
||||
renderObject
|
||||
..fit = fit
|
||||
..alignment = alignment;
|
||||
..alignment = alignment
|
||||
..textDirection = Directionality.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
description.add(new EnumProperty<BoxFit>('fit', fit));
|
||||
description.add(new DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,4 +112,230 @@ void main() {
|
||||
|
||||
expect(insidePoint, equals(outsidePoint));
|
||||
});
|
||||
|
||||
testWidgets('FittedBox with no child', (WidgetTester tester) async {
|
||||
final Key key = new UniqueKey();
|
||||
await tester.pumpWidget(
|
||||
new Center(
|
||||
child: new FittedBox(
|
||||
key: key,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox box = tester.firstRenderObject(find.byKey(key));
|
||||
expect(box.size.width, 0.0);
|
||||
expect(box.size.height, 0.0);
|
||||
});
|
||||
|
||||
testWidgets('Child can be aligned multiple ways in a row', (WidgetTester tester) async {
|
||||
final Key outside = new UniqueKey();
|
||||
final Key inside = new UniqueKey();
|
||||
|
||||
{ // align RTL
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new FittedBox(
|
||||
key: outside,
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: new Container(
|
||||
key: inside,
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
|
||||
expect(outsideBox.size.width, 100.0);
|
||||
expect(outsideBox.size.height, 100.0);
|
||||
|
||||
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
|
||||
expect(insideBox.size.width, 10.0);
|
||||
expect(insideBox.size.height, 10.0);
|
||||
|
||||
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(0.0, 90.0));
|
||||
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
|
||||
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(10.0, 100.0));
|
||||
|
||||
expect(insideTopLeft, equals(outsideTopLeft));
|
||||
expect(insideBottomRight, equals(outsideBottomRight));
|
||||
}
|
||||
|
||||
{ // change direction
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new FittedBox(
|
||||
key: outside,
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: new Container(
|
||||
key: inside,
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
|
||||
expect(outsideBox.size.width, 100.0);
|
||||
expect(outsideBox.size.height, 100.0);
|
||||
|
||||
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
|
||||
expect(insideBox.size.width, 10.0);
|
||||
expect(insideBox.size.height, 10.0);
|
||||
|
||||
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(90.0, 90.0));
|
||||
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
|
||||
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
|
||||
|
||||
expect(insideTopLeft, equals(outsideTopLeft));
|
||||
expect(insideBottomRight, equals(outsideBottomRight));
|
||||
}
|
||||
|
||||
{ // change alignment
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new FittedBox(
|
||||
key: outside,
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: new Container(
|
||||
key: inside,
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
|
||||
expect(outsideBox.size.width, 100.0);
|
||||
expect(outsideBox.size.height, 100.0);
|
||||
|
||||
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
|
||||
expect(insideBox.size.width, 10.0);
|
||||
expect(insideBox.size.height, 10.0);
|
||||
|
||||
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(45.0, 45.0));
|
||||
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
|
||||
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(55.0, 55.0));
|
||||
|
||||
expect(insideTopLeft, equals(outsideTopLeft));
|
||||
expect(insideBottomRight, equals(outsideBottomRight));
|
||||
}
|
||||
|
||||
{ // change size
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new FittedBox(
|
||||
key: outside,
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: new Container(
|
||||
key: inside,
|
||||
width: 30.0,
|
||||
height: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
|
||||
expect(outsideBox.size.width, 100.0);
|
||||
expect(outsideBox.size.height, 100.0);
|
||||
|
||||
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
|
||||
expect(insideBox.size.width, 30.0);
|
||||
expect(insideBox.size.height, 10.0);
|
||||
|
||||
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(35.0, 45.0));
|
||||
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
|
||||
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(65.0, 55.0));
|
||||
|
||||
expect(insideTopLeft, equals(outsideTopLeft));
|
||||
expect(insideBottomRight, equals(outsideBottomRight));
|
||||
}
|
||||
|
||||
{ // change fit
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: new FittedBox(
|
||||
key: outside,
|
||||
fit: BoxFit.fill,
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: new Container(
|
||||
key: inside,
|
||||
width: 30.0,
|
||||
height: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
|
||||
expect(outsideBox.size.width, 100.0);
|
||||
expect(outsideBox.size.height, 100.0);
|
||||
|
||||
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
|
||||
expect(insideBox.size.width, 30.0);
|
||||
expect(insideBox.size.height, 10.0);
|
||||
|
||||
final Offset insideTopLeft = insideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(0.0, 0.0));
|
||||
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
|
||||
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
|
||||
|
||||
expect(insideTopLeft, equals(outsideTopLeft));
|
||||
expect(insideBottomRight, equals(outsideBottomRight));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user