Fix non-flexible Chip in a Row. (#11669)
This commit is contained in:
parent
58163e0c39
commit
ce035d09c6
@ -50,7 +50,7 @@ void debugPrintThrottled(String message, { int wrapWidth }) {
|
||||
_debugPrintTask();
|
||||
}
|
||||
int _debugPrintedCharacters = 0;
|
||||
const int _kDebugPrintCapacity = 16 * 1024;
|
||||
const int _kDebugPrintCapacity = 12 * 1024;
|
||||
const Duration _kDebugPrintPauseTime = const Duration(seconds: 1);
|
||||
final Queue<String> _debugPrintBuffer = new Queue<String>();
|
||||
final Stopwatch _debugPrintStopwatch = new Stopwatch();
|
||||
|
@ -72,6 +72,11 @@ enum MainAxisSize {
|
||||
/// If the incoming layout constraints have a large enough
|
||||
/// [BoxConstraints.minWidth] or [BoxConstraints.minHeight], there might still
|
||||
/// be a non-zero amount of free space.
|
||||
///
|
||||
/// If the incoming layout constraints are unbounded, and any children have a
|
||||
/// non-zero [FlexParentData.flex] and a [FlexFit.tight] fit (as applied by
|
||||
/// [Expanded]), the [RenderFlex] will assert, because there would be infinite
|
||||
/// remaining free space and boxes cannot be given infinite size.
|
||||
min,
|
||||
|
||||
/// Maximize the amount of free space along the main axis, subject to the
|
||||
@ -80,6 +85,10 @@ enum MainAxisSize {
|
||||
/// If the incoming layout constraints have a small enough
|
||||
/// [BoxConstraints.maxWidth] or [BoxConstraints.maxHeight], there might still
|
||||
/// be no free space.
|
||||
///
|
||||
/// If the incoming layout constraints are unbounded, the [RenderFlex] will
|
||||
/// assert, because there would be infinite remaining free space and boxes
|
||||
/// cannot be given infinite size.
|
||||
max,
|
||||
}
|
||||
|
||||
@ -465,11 +474,11 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
final String dimension = _direction == Axis.horizontal ? 'width' : 'height';
|
||||
String error, message;
|
||||
String addendum = '';
|
||||
if (maxMainSize == double.INFINITY) {
|
||||
if (!canFlex && (mainAxisSize == MainAxisSize.max || _getFit(child) == FlexFit.tight)) {
|
||||
error = 'RenderFlex children have non-zero flex but incoming $dimension constraints are unbounded.';
|
||||
message = 'When a $identity is in a parent that does not provide a finite $dimension constraint, for example '
|
||||
'if it is in a $axis scrollable, it will try to shrink-wrap its children along the $axis '
|
||||
'axis. Setting a flex on a child (e.g. using a Flexible) indicates that the child is to '
|
||||
'axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to '
|
||||
'expand to fill the remaining space in the $axis direction.';
|
||||
final StringBuffer information = new StringBuffer();
|
||||
RenderBox node = this;
|
||||
@ -502,6 +511,11 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
'$message\n'
|
||||
'These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child '
|
||||
'cannot simultaneously expand to fit its parent.\n'
|
||||
'Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible '
|
||||
'children (using Flexible rather than Expanded). This will allow the flexible children '
|
||||
'to size themselves to less than the infinite remaining space they would otherwise be '
|
||||
'forced to take, and then will cause the RenderFlex to shrink-wrap the children '
|
||||
'rather than expanding to fit the maximum constraints provided by the parent.\n'
|
||||
'The affected RenderFlex is:\n'
|
||||
' $this\n'
|
||||
'The creator information is set to:\n'
|
||||
@ -545,21 +559,21 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
assert(child.parentData == childParentData);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
_overflow = math.max(0.0, allocatedSize - (canFlex ? maxMainSize : 0.0));
|
||||
|
||||
// Distribute free space to flexible children, and determine baseline.
|
||||
final double freeSpace = math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize);
|
||||
double maxBaselineDistance = 0.0;
|
||||
if (totalFlex > 0 || crossAxisAlignment == CrossAxisAlignment.baseline) {
|
||||
final double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
|
||||
final double spacePerFlex = canFlex && totalFlex > 0 ? (freeSpace / totalFlex) : double.NAN;
|
||||
child = firstChild;
|
||||
while (child != null) {
|
||||
final int flex = _getFlex(child);
|
||||
if (flex > 0) {
|
||||
final double maxChildExtent = spacePerFlex * flex;
|
||||
final double maxChildExtent = canFlex ? spacePerFlex * flex : double.INFINITY;
|
||||
double minChildExtent;
|
||||
switch (_getFit(child)) {
|
||||
case FlexFit.tight:
|
||||
assert(maxChildExtent < double.INFINITY);
|
||||
minChildExtent = maxChildExtent;
|
||||
break;
|
||||
case FlexFit.loose:
|
||||
@ -617,43 +631,43 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
}
|
||||
|
||||
// Align items along the main axis.
|
||||
double leadingSpace;
|
||||
double betweenSpace;
|
||||
double remainingSpace;
|
||||
double actualSizeDelta;
|
||||
if (canFlex) {
|
||||
final bool isMainAxisSizeMax = mainAxisSize == MainAxisSize.max;
|
||||
final double preferredSize = isMainAxisSizeMax ? maxMainSize : allocatedSize;
|
||||
switch (_direction) {
|
||||
case Axis.horizontal:
|
||||
size = constraints.constrain(new Size(preferredSize, crossSize));
|
||||
remainingSpace = math.max(0.0, size.width - allocatedSize);
|
||||
actualSizeDelta = size.width - allocatedSize;
|
||||
crossSize = size.height;
|
||||
assert(isMainAxisSizeMax ? size.width == maxMainSize : size.width >= constraints.minWidth);
|
||||
break;
|
||||
case Axis.vertical:
|
||||
size = constraints.constrain(new Size(crossSize, preferredSize));
|
||||
remainingSpace = math.max(0.0, size.height - allocatedSize);
|
||||
actualSizeDelta = size.height - allocatedSize;
|
||||
crossSize = size.width;
|
||||
assert(isMainAxisSizeMax ? size.height == maxMainSize : size.height >= constraints.minHeight);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
leadingSpace = 0.0;
|
||||
betweenSpace = 0.0;
|
||||
switch (_direction) {
|
||||
case Axis.horizontal:
|
||||
size = constraints.constrain(new Size(_overflow, crossSize));
|
||||
size = constraints.constrain(new Size(allocatedSize, crossSize));
|
||||
crossSize = size.height;
|
||||
remainingSpace = math.max(0.0, size.width - _overflow);
|
||||
actualSizeDelta = size.width - allocatedSize;
|
||||
break;
|
||||
case Axis.vertical:
|
||||
size = constraints.constrain(new Size(crossSize, _overflow));
|
||||
size = constraints.constrain(new Size(crossSize, allocatedSize));
|
||||
crossSize = size.width;
|
||||
remainingSpace = math.max(0.0, size.height - _overflow);
|
||||
actualSizeDelta = size.height - allocatedSize;
|
||||
break;
|
||||
}
|
||||
_overflow = 0.0;
|
||||
}
|
||||
_overflow = math.max(0.0, -actualSizeDelta);
|
||||
|
||||
final double remainingSpace = math.max(0.0, actualSizeDelta);
|
||||
double leadingSpace;
|
||||
double betweenSpace;
|
||||
switch (_mainAxisAlignment) {
|
||||
case MainAxisAlignment.start:
|
||||
leadingSpace = 0.0;
|
||||
|
@ -180,4 +180,47 @@ void main() {
|
||||
onDeleted: () {},
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Chip in row works ok', (WidgetTester tester) async {
|
||||
final TextStyle style = new TextStyle(fontFamily: 'Ahem', fontSize: 10.0);
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Material(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Chip(label: new Text('Test'), labelStyle: style),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
|
||||
expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0));
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Material(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Flexible(child: new Chip(label: new Text('Test'), labelStyle: style)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
|
||||
expect(tester.getSize(find.byType(Chip)), const Size(64.0, 32.0));
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Material(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Expanded(child: new Chip(label: new Text('Test'), labelStyle: style)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(tester.getSize(find.byType(Text)), const Size(40.0, 10.0));
|
||||
expect(tester.getSize(find.byType(Chip)), const Size(800.0, 32.0));
|
||||
});
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@ -75,7 +76,7 @@ void main() {
|
||||
expect(flex.getMaxIntrinsicWidth(200.0), equals(0.0));
|
||||
});
|
||||
|
||||
// We can't right a horizontal version of the above test due to
|
||||
// We can't write a horizontal version of the above test due to
|
||||
// RenderAspectRatio being height-in, width-out.
|
||||
|
||||
test('Defaults', () {
|
||||
@ -284,4 +285,102 @@ void main() {
|
||||
expect(box3.size.width, equals(100.0));
|
||||
expect(flex.size.width, equals(300.0));
|
||||
});
|
||||
|
||||
test('MainAxisSize.min inside unconstrained', () {
|
||||
FlutterError.onError = (FlutterErrorDetails details) => throw details.exception;
|
||||
final BoxConstraints square = const BoxConstraints.tightFor(width: 100.0, height: 100.0);
|
||||
final RenderConstrainedBox box1 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box2 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box3 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderFlex flex = new RenderFlex(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
);
|
||||
final RenderConstrainedOverflowBox parent = new RenderConstrainedOverflowBox(
|
||||
minWidth: 0.0,
|
||||
maxWidth: double.INFINITY,
|
||||
minHeight: 0.0,
|
||||
maxHeight: 400.0,
|
||||
child: flex,
|
||||
);
|
||||
flex.addAll(<RenderBox>[box1, box2, box3]);
|
||||
layout(parent);
|
||||
expect(flex.size, const Size(300.0, 100.0));
|
||||
final FlexParentData box2ParentData = box2.parentData;
|
||||
box2ParentData.flex = 1;
|
||||
box2ParentData.fit = FlexFit.loose;
|
||||
flex.markNeedsLayout();
|
||||
pumpFrame();
|
||||
expect(flex.size, const Size(300.0, 100.0));
|
||||
parent.maxWidth = 500.0; // NOW WITH CONSTRAINED BOUNDARIES
|
||||
pumpFrame();
|
||||
expect(flex.size, const Size(300.0, 100.0));
|
||||
flex.mainAxisSize = MainAxisSize.max;
|
||||
pumpFrame();
|
||||
expect(flex.size, const Size(500.0, 100.0));
|
||||
flex.mainAxisSize = MainAxisSize.min;
|
||||
box2ParentData.fit = FlexFit.tight;
|
||||
flex.markNeedsLayout();
|
||||
pumpFrame();
|
||||
expect(flex.size, const Size(500.0, 100.0));
|
||||
parent.maxWidth = 505.0;
|
||||
pumpFrame();
|
||||
expect(flex.size, const Size(505.0, 100.0));
|
||||
});
|
||||
|
||||
test('MainAxisSize.min inside unconstrained', () {
|
||||
final List<dynamic> exceptions = <dynamic>[];
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
exceptions.add(details.exception);
|
||||
};
|
||||
final BoxConstraints square = const BoxConstraints.tightFor(width: 100.0, height: 100.0);
|
||||
final RenderConstrainedBox box1 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box2 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box3 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderFlex flex = new RenderFlex(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
);
|
||||
final RenderConstrainedOverflowBox parent = new RenderConstrainedOverflowBox(
|
||||
minWidth: 0.0,
|
||||
maxWidth: double.INFINITY,
|
||||
minHeight: 0.0,
|
||||
maxHeight: 400.0,
|
||||
child: flex,
|
||||
);
|
||||
flex.addAll(<RenderBox>[box1, box2, box3]);
|
||||
final FlexParentData box2ParentData = box2.parentData;
|
||||
box2ParentData.flex = 1;
|
||||
expect(exceptions, isEmpty);
|
||||
layout(parent);
|
||||
expect(exceptions, isNotEmpty);
|
||||
expect(exceptions.first, new isInstanceOf<FlutterError>());
|
||||
});
|
||||
|
||||
test('MainAxisSize.min inside unconstrained', () {
|
||||
final List<dynamic> exceptions = <dynamic>[];
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
exceptions.add(details.exception);
|
||||
};
|
||||
final BoxConstraints square = const BoxConstraints.tightFor(width: 100.0, height: 100.0);
|
||||
final RenderConstrainedBox box1 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box2 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderConstrainedBox box3 = new RenderConstrainedBox(additionalConstraints: square);
|
||||
final RenderFlex flex = new RenderFlex(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
);
|
||||
final RenderConstrainedOverflowBox parent = new RenderConstrainedOverflowBox(
|
||||
minWidth: 0.0,
|
||||
maxWidth: double.INFINITY,
|
||||
minHeight: 0.0,
|
||||
maxHeight: 400.0,
|
||||
child: flex,
|
||||
);
|
||||
flex.addAll(<RenderBox>[box1, box2, box3]);
|
||||
final FlexParentData box2ParentData = box2.parentData;
|
||||
box2ParentData.flex = 1;
|
||||
box2ParentData.fit = FlexFit.loose;
|
||||
expect(exceptions, isEmpty);
|
||||
layout(parent);
|
||||
expect(exceptions, isNotEmpty);
|
||||
expect(exceptions.first, new isInstanceOf<FlutterError>());
|
||||
});
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Can hit test flex children of stacks',
|
||||
(WidgetTester tester) async {
|
||||
testWidgets('Can hit test flex children of stacks', (WidgetTester tester) async {
|
||||
bool didReceiveTap = false;
|
||||
await tester.pumpWidget(
|
||||
new Container(
|
||||
|
Loading…
x
Reference in New Issue
Block a user