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() {
|
void performLayout() {
|
||||||
// Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths
|
// Originally 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
|
|
||||||
|
// Determine used flex factor, size inflexible items, calculate free space.
|
||||||
int totalFlex = 0;
|
int totalFlex = 0;
|
||||||
int totalChildren = 0;
|
int totalChildren = 0;
|
||||||
assert(constraints != null);
|
assert(constraints != null);
|
||||||
final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.maxWidth : constraints.maxHeight;
|
final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.maxWidth : constraints.maxHeight;
|
||||||
double crossSize = 0.0; // This will be determined after laying out the children
|
final bool canFlex = mainSize < double.INFINITY;
|
||||||
double freeSpace = mainSize;
|
double crossSize = 0.0; // This is determined as we lay out the children
|
||||||
|
double freeSpace = canFlex ? mainSize : 0.0;
|
||||||
RenderBox child = firstChild;
|
RenderBox child = firstChild;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
assert(child.parentData is FlexBoxParentData);
|
assert(child.parentData is FlexBoxParentData);
|
||||||
totalChildren++;
|
totalChildren++;
|
||||||
int flex = _getFlex(child);
|
int flex = _getFlex(child);
|
||||||
if (flex > 0) {
|
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;
|
totalFlex += child.parentData.flex;
|
||||||
} else {
|
} else {
|
||||||
BoxConstraints innerConstraints;
|
BoxConstraints innerConstraints;
|
||||||
if (alignItems == FlexAlignItems.stretch) {
|
if (alignItems == FlexAlignItems.stretch) {
|
||||||
switch (_direction) {
|
switch (_direction) {
|
||||||
case FlexDirection.horizontal:
|
case FlexDirection.horizontal:
|
||||||
innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth,
|
innerConstraints = new BoxConstraints(minHeight: constraints.minHeight,
|
||||||
minHeight: constraints.minHeight,
|
|
||||||
maxHeight: constraints.maxHeight);
|
maxHeight: constraints.maxHeight);
|
||||||
break;
|
break;
|
||||||
case FlexDirection.vertical:
|
case FlexDirection.vertical:
|
||||||
innerConstraints = new BoxConstraints(minWidth: constraints.minWidth,
|
innerConstraints = new BoxConstraints(minWidth: constraints.minWidth,
|
||||||
maxWidth: constraints.maxWidth,
|
maxWidth: constraints.maxWidth);
|
||||||
maxHeight: constraints.maxHeight);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
child.layout(innerConstraints, parentUsesSize: true);
|
||||||
freeSpace -= _getMainSize(child);
|
freeSpace -= _getMainSize(child);
|
||||||
@ -331,10 +342,11 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
|||||||
_overflow = math.max(0.0, -freeSpace);
|
_overflow = math.max(0.0, -freeSpace);
|
||||||
freeSpace = math.max(0.0, freeSpace);
|
freeSpace = math.max(0.0, freeSpace);
|
||||||
|
|
||||||
// Steps 4-5. Distribute remaining space to flexible children.
|
// Distribute remaining space to flexible children, and determine baseline.
|
||||||
double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
|
|
||||||
double usedSpace = 0.0;
|
|
||||||
double maxBaselineDistance = 0.0;
|
double maxBaselineDistance = 0.0;
|
||||||
|
double usedSpace = 0.0;
|
||||||
|
if (totalFlex > 0 || alignItems == FlexAlignItems.baseline) {
|
||||||
|
double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
|
||||||
child = firstChild;
|
child = firstChild;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
int flex = _getFlex(child);
|
int flex = _getFlex(child);
|
||||||
@ -383,11 +395,45 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
|||||||
assert(child.parentData is FlexBoxParentData);
|
assert(child.parentData is FlexBoxParentData);
|
||||||
child = child.parentData.nextSibling;
|
child = child.parentData.nextSibling;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Section 8.2: Main Axis Alignment using the justify-content property
|
// Align items along the main axis.
|
||||||
double remainingSpace = math.max(0.0, freeSpace - usedSpace);
|
|
||||||
double leadingSpace;
|
double leadingSpace;
|
||||||
double betweenSpace;
|
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) {
|
switch (_justifyContent) {
|
||||||
case FlexJustifyContent.start:
|
case FlexJustifyContent.start:
|
||||||
leadingSpace = 0.0;
|
leadingSpace = 0.0;
|
||||||
@ -411,17 +457,6 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
|||||||
break;
|
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
|
// Position elements
|
||||||
double childMainPosition = leadingSpace;
|
double childMainPosition = leadingSpace;
|
||||||
child = firstChild;
|
child = firstChild;
|
||||||
@ -517,4 +552,6 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
|
|||||||
return header;
|
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)
|
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([
|
return new Stack([
|
||||||
new Listener(
|
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:collection';
|
||||||
import 'dart:sky' as sky;
|
import 'dart:sky' as sky;
|
||||||
|
|
||||||
import 'package:sky/base/debug.dart';
|
|
||||||
import 'package:sky/base/hit_test.dart';
|
import 'package:sky/base/hit_test.dart';
|
||||||
import 'package:sky/base/scheduler.dart' as scheduler;
|
import 'package:sky/base/scheduler.dart' as scheduler;
|
||||||
import 'package:sky/mojo/activity.dart';
|
import 'package:sky/mojo/activity.dart';
|
||||||
|
@ -46,9 +46,12 @@ class ToolBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> children = new List<Widget>();
|
List<Widget> children = new List<Widget>();
|
||||||
|
|
||||||
|
// left children
|
||||||
if (left != null)
|
if (left != null)
|
||||||
children.add(left);
|
children.add(left);
|
||||||
|
|
||||||
|
// center children (left-aligned, but takes all remaining space)
|
||||||
children.add(
|
children.add(
|
||||||
new Flexible(
|
new Flexible(
|
||||||
child: new Padding(
|
child: new Padding(
|
||||||
@ -58,15 +61,21 @@ class ToolBar extends Component {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// right children
|
||||||
if (right != null)
|
if (right != null)
|
||||||
children.addAll(right);
|
children.addAll(right);
|
||||||
|
|
||||||
Widget content = new Container(
|
Widget content = new Container(
|
||||||
child: new DefaultTextStyle(
|
child: new DefaultTextStyle(
|
||||||
style: sideStyle,
|
style: sideStyle,
|
||||||
child: new Flex(
|
child: new Flex([
|
||||||
[new Container(child: new Flex(children), height: kToolBarHeight)],
|
new Container(
|
||||||
alignItems: FlexAlignItems.end
|
child: new Flex(children),
|
||||||
|
height: kToolBarHeight
|
||||||
|
),
|
||||||
|
],
|
||||||
|
direction: FlexDirection.vertical,
|
||||||
|
justifyContent: FlexJustifyContent.end
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
padding: new EdgeDims.symmetric(horizontal: 8.0),
|
padding: new EdgeDims.symmetric(horizontal: 8.0),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user