Make Flex shrink-wrap when unconstrained.
This commit is contained in:
parent
fe7e39b23c
commit
14f3f58cd9
@ -289,38 +289,49 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
}
|
||||
|
||||
void performLayout() {
|
||||
// Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths
|
||||
// Steps 1-3. Determine used flex factor, size inflexible items, calculate free space
|
||||
// Originally based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths
|
||||
|
||||
// Determine used flex factor, size inflexible items, calculate free space.
|
||||
int totalFlex = 0;
|
||||
int totalChildren = 0;
|
||||
assert(constraints != null);
|
||||
final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.maxWidth : constraints.maxHeight;
|
||||
double crossSize = 0.0; // This will be determined after laying out the children
|
||||
double freeSpace = mainSize;
|
||||
final bool canFlex = mainSize < double.INFINITY;
|
||||
double crossSize = 0.0; // This is determined as we lay out the children
|
||||
double freeSpace = canFlex ? mainSize : 0.0;
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
assert(child.parentData is FlexBoxParentData);
|
||||
totalChildren++;
|
||||
int flex = _getFlex(child);
|
||||
if (flex > 0) {
|
||||
// Flexible children can only be used when the RenderFlex box's container has a finite size.
|
||||
// When the container is infinite, for example if you are in a scrollable viewport, then
|
||||
// it wouldn't make any sense to have a flexible child.
|
||||
assert(canFlex && 'See https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/lib/widgets/flex.md' is String);
|
||||
totalFlex += child.parentData.flex;
|
||||
} else {
|
||||
BoxConstraints innerConstraints;
|
||||
if (alignItems == FlexAlignItems.stretch) {
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth,
|
||||
minHeight: constraints.minHeight,
|
||||
innerConstraints = new BoxConstraints(minHeight: constraints.minHeight,
|
||||
maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(minWidth: constraints.minWidth,
|
||||
maxWidth: constraints.maxWidth,
|
||||
maxHeight: constraints.maxHeight);
|
||||
maxWidth: constraints.maxWidth);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
innerConstraints = constraints.loosen();
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
child.layout(innerConstraints, parentUsesSize: true);
|
||||
freeSpace -= _getMainSize(child);
|
||||
@ -331,63 +342,98 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
_overflow = math.max(0.0, -freeSpace);
|
||||
freeSpace = math.max(0.0, freeSpace);
|
||||
|
||||
// Steps 4-5. Distribute remaining space to flexible children.
|
||||
double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
|
||||
double usedSpace = 0.0;
|
||||
// Distribute remaining space to flexible children, and determine baseline.
|
||||
double maxBaselineDistance = 0.0;
|
||||
child = firstChild;
|
||||
while (child != null) {
|
||||
int flex = _getFlex(child);
|
||||
if (flex > 0) {
|
||||
double spaceForChild = spacePerFlex * flex;
|
||||
BoxConstraints innerConstraints;
|
||||
if (alignItems == FlexAlignItems.stretch) {
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(minWidth: spaceForChild,
|
||||
maxWidth: spaceForChild,
|
||||
minHeight: constraints.maxHeight,
|
||||
maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minHeight: spaceForChild,
|
||||
maxHeight: spaceForChild);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(minWidth: spaceForChild,
|
||||
maxWidth: spaceForChild,
|
||||
maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth,
|
||||
minHeight: spaceForChild,
|
||||
maxHeight: spaceForChild);
|
||||
break;
|
||||
double usedSpace = 0.0;
|
||||
if (totalFlex > 0 || alignItems == FlexAlignItems.baseline) {
|
||||
double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
|
||||
child = firstChild;
|
||||
while (child != null) {
|
||||
int flex = _getFlex(child);
|
||||
if (flex > 0) {
|
||||
double spaceForChild = spacePerFlex * flex;
|
||||
BoxConstraints innerConstraints;
|
||||
if (alignItems == FlexAlignItems.stretch) {
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(minWidth: spaceForChild,
|
||||
maxWidth: spaceForChild,
|
||||
minHeight: constraints.maxHeight,
|
||||
maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
|
||||
maxWidth: constraints.maxWidth,
|
||||
minHeight: spaceForChild,
|
||||
maxHeight: spaceForChild);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
innerConstraints = new BoxConstraints(minWidth: spaceForChild,
|
||||
maxWidth: spaceForChild,
|
||||
maxHeight: constraints.maxHeight);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth,
|
||||
minHeight: spaceForChild,
|
||||
maxHeight: spaceForChild);
|
||||
break;
|
||||
}
|
||||
}
|
||||
child.layout(innerConstraints, parentUsesSize: true);
|
||||
usedSpace += _getMainSize(child);
|
||||
crossSize = math.max(crossSize, _getCrossSize(child));
|
||||
}
|
||||
child.layout(innerConstraints, parentUsesSize: true);
|
||||
usedSpace += _getMainSize(child);
|
||||
crossSize = math.max(crossSize, _getCrossSize(child));
|
||||
if (alignItems == FlexAlignItems.baseline) {
|
||||
assert(textBaseline != null);
|
||||
double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
|
||||
if (distance != null)
|
||||
maxBaselineDistance = math.max(maxBaselineDistance, distance);
|
||||
}
|
||||
assert(child.parentData is FlexBoxParentData);
|
||||
child = child.parentData.nextSibling;
|
||||
}
|
||||
if (alignItems == FlexAlignItems.baseline) {
|
||||
assert(textBaseline != null);
|
||||
double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
|
||||
if (distance != null)
|
||||
maxBaselineDistance = math.max(maxBaselineDistance, distance);
|
||||
}
|
||||
assert(child.parentData is FlexBoxParentData);
|
||||
child = child.parentData.nextSibling;
|
||||
}
|
||||
|
||||
// Section 8.2: Main Axis Alignment using the justify-content property
|
||||
double remainingSpace = math.max(0.0, freeSpace - usedSpace);
|
||||
// Align items along the main axis.
|
||||
double leadingSpace;
|
||||
double betweenSpace;
|
||||
double remainingSpace;
|
||||
if (canFlex) {
|
||||
remainingSpace = math.max(0.0, freeSpace - usedSpace);
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
size = constraints.constrain(new Size(mainSize, crossSize));
|
||||
crossSize = size.height;
|
||||
assert(size.width == mainSize);
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
size = constraints.constrain(new Size(crossSize, mainSize));
|
||||
crossSize = size.width;
|
||||
assert(size.height == mainSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
leadingSpace = 0.0;
|
||||
betweenSpace = 0.0;
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
size = constraints.constrain(new Size(-_overflow, crossSize));
|
||||
crossSize = size.height;
|
||||
assert(size.width >= -_overflow);
|
||||
remainingSpace = size.width - -_overflow;
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
size = constraints.constrain(new Size(crossSize, -_overflow));
|
||||
crossSize = size.width;
|
||||
assert(size.height >= -_overflow);
|
||||
remainingSpace = size.height - -_overflow;
|
||||
break;
|
||||
}
|
||||
_overflow = 0.0;
|
||||
}
|
||||
switch (_justifyContent) {
|
||||
case FlexJustifyContent.start:
|
||||
leadingSpace = 0.0;
|
||||
@ -411,17 +457,6 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_direction) {
|
||||
case FlexDirection.horizontal:
|
||||
size = constraints.constrain(new Size(mainSize, crossSize));
|
||||
crossSize = size.height;
|
||||
break;
|
||||
case FlexDirection.vertical:
|
||||
size = constraints.constrain(new Size(crossSize, mainSize));
|
||||
crossSize = size.width;
|
||||
break;
|
||||
}
|
||||
|
||||
// Position elements
|
||||
double childMainPosition = leadingSpace;
|
||||
child = firstChild;
|
||||
@ -517,4 +552,6 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
||||
return header;
|
||||
}
|
||||
|
||||
String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}direction: ${_direction}\n${prefix}justifyContent: ${_justifyContent}\n${prefix}alignItems: ${_alignItems}\n${prefix}textBaseline: ${_textBaseline}\n';
|
||||
|
||||
}
|
||||
|
@ -79,7 +79,11 @@ class Dialog extends Component {
|
||||
}
|
||||
|
||||
if (actions != null)
|
||||
dialogBody.add(new Flex(actions, justifyContent: FlexJustifyContent.end));
|
||||
dialogBody.add(new Container(
|
||||
child: new Flex(actions,
|
||||
justifyContent: FlexJustifyContent.end
|
||||
)
|
||||
));
|
||||
|
||||
return new Stack([
|
||||
new Listener(
|
||||
|
70
packages/flutter/lib/widgets/flex.md
Normal file
70
packages/flutter/lib/widgets/flex.md
Normal file
@ -0,0 +1,70 @@
|
||||
How To Use Flex In Sky
|
||||
======================
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
In Sky, widgets are rendered by render boxes. Render boxes are given
|
||||
constraints by their parent, and size themselves within those
|
||||
constraints. Constraints consist of minimum and maximum widths and
|
||||
heights; sizes consist of a specific width and height.
|
||||
|
||||
Generally, there are three kinds of boxes, in terms of how they handle
|
||||
their constraints:
|
||||
|
||||
- Those that try to be as big as possible.
|
||||
For example, the boxes used by `Center` and `Block`.
|
||||
- Those that try to be the same size as their children.
|
||||
For example, the boxes used by `Transform` and `Opacity`.
|
||||
- Those that try to be a particular size.
|
||||
For example, the boxes used by `Image` and `Text`.
|
||||
|
||||
Some widgets, for example `Container`, vary from type to type based on
|
||||
their constructor arguments. In the case of `Container`, it defaults
|
||||
to trying to be as big as possible, but if you give it a `width`, for
|
||||
instance, it tries to honor that and be that particular size.
|
||||
|
||||
The constraints are sometimes "tight", meaning that they leave no room
|
||||
for the render box to decide on a size (e.g. if the minimum and
|
||||
maximum width are the same, it is said to have a tight width). The
|
||||
main example of this is the `App` widget, which is contained by the
|
||||
`RenderView` class: the box used by the child returned by the
|
||||
application's `build` function is given a constraint that forces it to
|
||||
exactly fill the application's content area (typically, the entire
|
||||
screen).
|
||||
|
||||
Unbounded constraints
|
||||
---------------------
|
||||
|
||||
In certain situations, the constraint that is given to a box will be
|
||||
_unbounded_, or infinite. This means that either the maximum width or
|
||||
the maximum height is set to `double.INFINITY`.
|
||||
|
||||
A box that tries to be as big as possible won't function usefully when
|
||||
given an unbounded constraint, and in checked mode, will assert.
|
||||
|
||||
The most common cases where a render box finds itself with unbounded
|
||||
constraints are within flex boxes (`Row` and `Column`), and **within
|
||||
scrollable regions** (mainly `Block`, `ScollableList<T>`, and
|
||||
`ScrollableMixedWidgetList`).
|
||||
|
||||
Flex
|
||||
----
|
||||
|
||||
Flex boxes themselves (`Row` and `Column`) behave differently based on
|
||||
whether they are in a bounded constraints or unbounded constraints in
|
||||
their given direction.
|
||||
|
||||
In bounded constraints, they try to be as big as possible in that
|
||||
direction.
|
||||
|
||||
In unbounded constraints, they try to fit their children in that
|
||||
direction. In this case, you cannot set `flex` on the children to
|
||||
anything other than 0 (the default). In the widget hierarchy, this
|
||||
means that you cannot use `Flexible` when the flex box is inside
|
||||
another flex box or inside a scrollable.
|
||||
|
||||
In the _cross_ direction, i.e. in their width for `Column` (vertical
|
||||
flex) and in their height for `Row` (horizontal flex), they must never
|
||||
be unbounded, otherwise they would not be able to reasonably align
|
||||
their children.
|
@ -6,7 +6,6 @@ import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:sky' as sky;
|
||||
|
||||
import 'package:sky/base/debug.dart';
|
||||
import 'package:sky/base/hit_test.dart';
|
||||
import 'package:sky/base/scheduler.dart' as scheduler;
|
||||
import 'package:sky/mojo/activity.dart';
|
||||
|
@ -46,9 +46,12 @@ class ToolBar extends Component {
|
||||
}
|
||||
|
||||
List<Widget> children = new List<Widget>();
|
||||
|
||||
// left children
|
||||
if (left != null)
|
||||
children.add(left);
|
||||
|
||||
// center children (left-aligned, but takes all remaining space)
|
||||
children.add(
|
||||
new Flexible(
|
||||
child: new Padding(
|
||||
@ -58,15 +61,21 @@ class ToolBar extends Component {
|
||||
)
|
||||
);
|
||||
|
||||
// right children
|
||||
if (right != null)
|
||||
children.addAll(right);
|
||||
|
||||
Widget content = new Container(
|
||||
child: new DefaultTextStyle(
|
||||
style: sideStyle,
|
||||
child: new Flex(
|
||||
[new Container(child: new Flex(children), height: kToolBarHeight)],
|
||||
alignItems: FlexAlignItems.end
|
||||
child: new Flex([
|
||||
new Container(
|
||||
child: new Flex(children),
|
||||
height: kToolBarHeight
|
||||
),
|
||||
],
|
||||
direction: FlexDirection.vertical,
|
||||
justifyContent: FlexJustifyContent.end
|
||||
)
|
||||
),
|
||||
padding: new EdgeDims.symmetric(horizontal: 8.0),
|
||||
|
Loading…
x
Reference in New Issue
Block a user