mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add support for autolayout to widgets
This patch teaches the widget framework how to use Cassowary-based autolayout. To integrate autolayout with widgets, I had to refactor how RenderAutoLayout worked a bit. Now RenderAutoLayout follows the same delegate pattern we use for custom paint and custom layout.
This commit is contained in:
parent
cdc4055444
commit
262dd7a63b
@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@ -8,6 +8,45 @@
|
||||
import 'package:cassowary/cassowary.dart' as al;
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
class _MyAutoLayoutDelegate extends AutoLayoutDelegate {
|
||||
AutoLayoutParams p1 = new AutoLayoutParams();
|
||||
AutoLayoutParams p2 = new AutoLayoutParams();
|
||||
AutoLayoutParams p3 = new AutoLayoutParams();
|
||||
AutoLayoutParams p4 = new AutoLayoutParams();
|
||||
|
||||
List<al.Constraint> getConstraints(AutoLayoutParams parentParams) {
|
||||
return <al.Constraint>[
|
||||
// Sum of widths of each box must be equal to that of the container
|
||||
(p1.width + p2.width + p3.width == parentParams.width) as al.Constraint,
|
||||
|
||||
// The boxes must be stacked left to right
|
||||
p1.rightEdge <= p2.leftEdge,
|
||||
p2.rightEdge <= p3.leftEdge,
|
||||
|
||||
// The widths of the first and the third boxes should be equal
|
||||
(p1.width == p3.width) as al.Constraint,
|
||||
|
||||
// The width of the second box should be twice as much as that of the first
|
||||
// and third
|
||||
(p2.width * al.cm(2.0) == p1.width) as al.Constraint,
|
||||
|
||||
// The height of the three boxes should be equal to that of the container
|
||||
(p1.height == p2.height) as al.Constraint,
|
||||
(p2.height == p3.height) as al.Constraint,
|
||||
(p3.height == parentParams.height) as al.Constraint,
|
||||
|
||||
// The fourth box should be half as wide as the second and must be attached
|
||||
// to the right edge of the same (by its center)
|
||||
(p4.width == p2.width / al.cm(2.0)) as al.Constraint,
|
||||
(p4.height == al.cm(50.0)) as al.Constraint,
|
||||
(p4.horizontalCenter == p2.rightEdge) as al.Constraint,
|
||||
(p4.verticalCenter == p2.height / al.cm(2.0)) as al.Constraint,
|
||||
];
|
||||
}
|
||||
|
||||
bool shouldUpdateConstraints(AutoLayoutDelegate oldDelegate) => true;
|
||||
}
|
||||
|
||||
void main() {
|
||||
RenderDecoratedBox c1 = new RenderDecoratedBox(
|
||||
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFF0000))
|
||||
@ -25,40 +64,22 @@ void main() {
|
||||
decoration: new BoxDecoration(backgroundColor: const Color(0xFFFFFFFF))
|
||||
);
|
||||
|
||||
RenderAutoLayout root = new RenderAutoLayout(children: <RenderBox>[c1, c2, c3, c4]);
|
||||
_MyAutoLayoutDelegate delegate = new _MyAutoLayoutDelegate();
|
||||
|
||||
AutoLayoutParentData p1 = c1.parentData;
|
||||
AutoLayoutParentData p2 = c2.parentData;
|
||||
AutoLayoutParentData p3 = c3.parentData;
|
||||
AutoLayoutParentData p4 = c4.parentData;
|
||||
RenderAutoLayout root = new RenderAutoLayout(
|
||||
delegate: delegate,
|
||||
children: <RenderBox>[c1, c2, c3, c4]
|
||||
);
|
||||
|
||||
root.addConstraints(<al.Constraint>[
|
||||
// Sum of widths of each box must be equal to that of the container
|
||||
(p1.width + p2.width + p3.width == root.width) as al.Constraint,
|
||||
AutoLayoutParentData parentData1 = c1.parentData;
|
||||
AutoLayoutParentData parentData2 = c2.parentData;
|
||||
AutoLayoutParentData parentData3 = c3.parentData;
|
||||
AutoLayoutParentData parentData4 = c4.parentData;
|
||||
|
||||
// The boxes must be stacked left to right
|
||||
p1.rightEdge <= p2.leftEdge,
|
||||
p2.rightEdge <= p3.leftEdge,
|
||||
|
||||
// The widths of the first and the third boxes should be equal
|
||||
(p1.width == p3.width) as al.Constraint,
|
||||
|
||||
// The width of the second box should be twice as much as that of the first
|
||||
// and third
|
||||
(p2.width * al.cm(2.0) == p1.width) as al.Constraint,
|
||||
|
||||
// The height of the three boxes should be equal to that of the container
|
||||
(p1.height == p2.height) as al.Constraint,
|
||||
(p2.height == p3.height) as al.Constraint,
|
||||
(p3.height == root.height) as al.Constraint,
|
||||
|
||||
// The fourth box should be half as wide as the second and must be attached
|
||||
// to the right edge of the same (by its center)
|
||||
(p4.width == p2.width / al.cm(2.0)) as al.Constraint,
|
||||
(p4.height == al.cm(50.0)) as al.Constraint,
|
||||
(p4.horizontalCenter == p2.rightEdge) as al.Constraint,
|
||||
(p4.verticalCenter == p2.height / al.cm(2.0)) as al.Constraint,
|
||||
]);
|
||||
parentData1.params = delegate.p1;
|
||||
parentData2.params = delegate.p2;
|
||||
parentData3.params = delegate.p3;
|
||||
parentData4.params = delegate.p4;
|
||||
|
||||
new RenderingFlutterBinding(root: root);
|
||||
}
|
||||
|
87
examples/layers/widgets/autolayout.dart
Normal file
87
examples/layers/widgets/autolayout.dart
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This example shows how to use the Cassowary autolayout system with widgets.
|
||||
|
||||
import 'package:cassowary/cassowary.dart' as al;
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class _MyAutoLayoutDelegate extends AutoLayoutDelegate {
|
||||
AutoLayoutParams p1 = new AutoLayoutParams();
|
||||
AutoLayoutParams p2 = new AutoLayoutParams();
|
||||
AutoLayoutParams p3 = new AutoLayoutParams();
|
||||
AutoLayoutParams p4 = new AutoLayoutParams();
|
||||
|
||||
List<al.Constraint> getConstraints(AutoLayoutParams parentParams) {
|
||||
return <al.Constraint>[
|
||||
// Sum of widths of each box must be equal to that of the container
|
||||
(p1.width + p2.width + p3.width == parentParams.width) as al.Constraint,
|
||||
|
||||
// The boxes must be stacked left to right
|
||||
p1.rightEdge <= p2.leftEdge,
|
||||
p2.rightEdge <= p3.leftEdge,
|
||||
|
||||
// The widths of the first and the third boxes should be equal
|
||||
(p1.width == p3.width) as al.Constraint,
|
||||
|
||||
// The width of the second box should be twice as much as that of the first
|
||||
// and third
|
||||
(p2.width * al.cm(2.0) == p1.width) as al.Constraint,
|
||||
|
||||
// The height of the three boxes should be equal to that of the container
|
||||
(p1.height == p2.height) as al.Constraint,
|
||||
(p2.height == p3.height) as al.Constraint,
|
||||
(p3.height == parentParams.height) as al.Constraint,
|
||||
|
||||
// The fourth box should be half as wide as the second and must be attached
|
||||
// to the right edge of the same (by its center)
|
||||
(p4.width == p2.width / al.cm(2.0)) as al.Constraint,
|
||||
(p4.height == al.cm(50.0)) as al.Constraint,
|
||||
(p4.horizontalCenter == p2.rightEdge) as al.Constraint,
|
||||
(p4.verticalCenter == p2.height / al.cm(2.0)) as al.Constraint,
|
||||
];
|
||||
}
|
||||
|
||||
bool shouldUpdateConstraints(AutoLayoutDelegate oldDelegate) => true;
|
||||
}
|
||||
|
||||
class ColoredBox extends StatelessComponent {
|
||||
ColoredBox({ Key key, this.params, this.color }) : super(key: key);
|
||||
|
||||
final AutoLayoutParams params;
|
||||
final Color color;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new AutoLayoutChild(
|
||||
params: params,
|
||||
child: new DecoratedBox(
|
||||
decoration: new BoxDecoration(backgroundColor: color)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ColoredBoxes extends StatefulComponent {
|
||||
_ColoredBoxesState createState() => new _ColoredBoxesState();
|
||||
}
|
||||
|
||||
class _ColoredBoxesState extends State<ColoredBoxes> {
|
||||
final _MyAutoLayoutDelegate delegate = new _MyAutoLayoutDelegate();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new AutoLayout(
|
||||
delegate: delegate,
|
||||
children: <Widget>[
|
||||
new ColoredBox(params: delegate.p1, color: const Color(0xFFFF0000)),
|
||||
new ColoredBox(params: delegate.p2, color: const Color(0xFF00FF00)),
|
||||
new ColoredBox(params: delegate.p3, color: const Color(0xFF0000FF)),
|
||||
new ColoredBox(params: delegate.p4, color: const Color(0xFFFFFFFF)),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
runApp(new ColoredBoxes());
|
||||
}
|
@ -9,24 +9,23 @@ import 'object.dart';
|
||||
|
||||
/// Hosts the edge parameters and vends useful methods to construct expressions
|
||||
/// for constraints. Also sets up and manages implicit constraints and edit
|
||||
/// variables. Used as a mixin by layout containers and parent data instances
|
||||
/// of render boxes taking part in auto layout.
|
||||
abstract class _AutoLayoutParamMixin {
|
||||
|
||||
void _setupLayoutParameters(dynamic context) {
|
||||
_leftEdge = new al.Param.withContext(context);
|
||||
_rightEdge = new al.Param.withContext(context);
|
||||
_topEdge = new al.Param.withContext(context);
|
||||
_bottomEdge = new al.Param.withContext(context);
|
||||
/// variables.
|
||||
class AutoLayoutParams {
|
||||
AutoLayoutParams() {
|
||||
_leftEdge = new al.Param.withContext(this);
|
||||
_rightEdge = new al.Param.withContext(this);
|
||||
_topEdge = new al.Param.withContext(this);
|
||||
_bottomEdge = new al.Param.withContext(this);
|
||||
}
|
||||
|
||||
/// The render box with which these parameters are associated.
|
||||
RenderBox _renderBox;
|
||||
|
||||
al.Param _leftEdge;
|
||||
al.Param _rightEdge;
|
||||
al.Param _topEdge;
|
||||
al.Param _bottomEdge;
|
||||
|
||||
List<al.Constraint> _implicitConstraints;
|
||||
|
||||
al.Param get leftEdge => _leftEdge;
|
||||
al.Param get rightEdge => _rightEdge;
|
||||
al.Param get topEdge => _topEdge;
|
||||
@ -38,153 +37,154 @@ abstract class _AutoLayoutParamMixin {
|
||||
al.Expression get horizontalCenter => (_leftEdge + _rightEdge) / al.cm(2.0);
|
||||
al.Expression get verticalCenter => (_topEdge + _bottomEdge) / al.cm(2.0);
|
||||
|
||||
void _setupEditVariablesInSolver(al.Solver solver, double priority) {
|
||||
solver.addEditVariables(<al.Variable>[
|
||||
_leftEdge.variable,
|
||||
_rightEdge.variable,
|
||||
_topEdge.variable,
|
||||
_bottomEdge.variable
|
||||
], priority);
|
||||
List<al.Constraint> _implicitConstraints;
|
||||
|
||||
void _addImplicitConstraints() {
|
||||
assert(_renderBox != null);
|
||||
if (_renderBox.parent == null)
|
||||
return;
|
||||
assert(_renderBox.parent is RenderAutoLayout);
|
||||
final RenderAutoLayout parent = _renderBox.parent;
|
||||
final AutoLayoutParentData parentData = _renderBox.parentData;
|
||||
final List<al.Constraint> implicit = parentData._constructImplicitConstraints();
|
||||
if (implicit == null || implicit.isEmpty)
|
||||
return;
|
||||
final al.Result result = parent._solver.addConstraints(implicit);
|
||||
assert(result == al.Result.success);
|
||||
parent.markNeedsLayout();
|
||||
_implicitConstraints = implicit;
|
||||
}
|
||||
|
||||
void _applyEditsAtSize(al.Solver solver, Size size) {
|
||||
solver.suggestValueForVariable(_leftEdge.variable, 0.0);
|
||||
solver.suggestValueForVariable(_topEdge.variable, 0.0);
|
||||
solver.suggestValueForVariable(_bottomEdge.variable, size.height);
|
||||
solver.suggestValueForVariable(_rightEdge.variable, size.width);
|
||||
void _removeImplicitConstraints() {
|
||||
assert(_renderBox != null);
|
||||
if (_renderBox.parent == null)
|
||||
return;
|
||||
if (_implicitConstraints == null || _implicitConstraints.isEmpty)
|
||||
return;
|
||||
assert(_renderBox.parent is RenderAutoLayout);
|
||||
final RenderAutoLayout parent = _renderBox.parent;
|
||||
final al.Result result = parent._solver.removeConstraints(_implicitConstraints);
|
||||
assert(result == al.Result.success);
|
||||
parent.markNeedsLayout();
|
||||
_implicitConstraints = null;
|
||||
}
|
||||
}
|
||||
|
||||
class AutoLayoutParentData extends ContainerBoxParentDataMixin<RenderBox> {
|
||||
AutoLayoutParentData(this._renderBox);
|
||||
|
||||
final RenderBox _renderBox;
|
||||
|
||||
AutoLayoutParams get params => _params;
|
||||
AutoLayoutParams _params;
|
||||
void set params(AutoLayoutParams value) {
|
||||
if (_params == value)
|
||||
return;
|
||||
if (_params != null) {
|
||||
_params._removeImplicitConstraints();
|
||||
_params._renderBox = null;
|
||||
}
|
||||
_params = value;
|
||||
if (_params != null) {
|
||||
assert(_params._renderBox == null);
|
||||
_params._renderBox = _renderBox;
|
||||
_params._addImplicitConstraints();
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies the parameter updates.
|
||||
///
|
||||
/// This method is called when the solver has updated at least one of the
|
||||
/// layout parameters of this object. The object is now responsible for
|
||||
/// applying this update to its other properties (if necessary).
|
||||
void _applyAutolayoutParameterUpdates();
|
||||
BoxConstraints get _constraints {
|
||||
return new BoxConstraints.tightFor(
|
||||
width: _params._rightEdge.value - _params._leftEdge.value,
|
||||
height: _params._bottomEdge.value - _params._topEdge.value
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the set of implicit constraints that need to be applied to all
|
||||
/// instances of this class when they are moved into a render object with an
|
||||
/// active solver. If no implicit constraints needs to be applied, the object
|
||||
/// may return null.
|
||||
List<al.Constraint> _constructImplicitConstraints();
|
||||
|
||||
void _setupImplicitConstraints(al.Solver solver) {
|
||||
List<al.Constraint> implicit = _constructImplicitConstraints();
|
||||
|
||||
if (implicit == null || implicit.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
al.Result result = solver.addConstraints(implicit);
|
||||
assert(result == al.Result.success);
|
||||
|
||||
_implicitConstraints = implicit;
|
||||
}
|
||||
|
||||
void _removeImplicitConstraints(al.Solver solver) {
|
||||
if (_implicitConstraints == null || _implicitConstraints.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
al.Result result = solver.removeConstraints(_implicitConstraints);
|
||||
assert(result == al.Result.success);
|
||||
|
||||
_implicitConstraints = null;
|
||||
List<al.Constraint> _constructImplicitConstraints() {
|
||||
return <al.Constraint>[
|
||||
_params._leftEdge >= al.cm(0.0), // The left edge must be positive.
|
||||
_params._rightEdge >= _params._leftEdge, // Width must be positive.
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class AutoLayoutParentData extends ContainerBoxParentDataMixin<RenderBox> with _AutoLayoutParamMixin {
|
||||
|
||||
AutoLayoutParentData(this._renderBox) {
|
||||
_setupLayoutParameters(this);
|
||||
}
|
||||
|
||||
final RenderBox _renderBox;
|
||||
|
||||
void _applyAutolayoutParameterUpdates() {
|
||||
// This is called by the parent's layout function
|
||||
// to lay our box out.
|
||||
assert(_renderBox.parentData == this);
|
||||
assert(() {
|
||||
final RenderAutoLayout parent = _renderBox.parent;
|
||||
assert(parent.debugDoingThisLayout);
|
||||
});
|
||||
BoxConstraints size = new BoxConstraints.tightFor(
|
||||
width: _rightEdge.value - _leftEdge.value,
|
||||
height: _bottomEdge.value - _topEdge.value
|
||||
);
|
||||
_renderBox.layout(size);
|
||||
offset = new Offset(_leftEdge.value, _topEdge.value);
|
||||
}
|
||||
|
||||
List<al.Constraint> _constructImplicitConstraints() {
|
||||
return <al.Constraint>[
|
||||
_leftEdge >= al.cm(0.0), // The left edge must be positive.
|
||||
_rightEdge >= _leftEdge, // Width must be positive.
|
||||
];
|
||||
}
|
||||
abstract class AutoLayoutDelegate {
|
||||
const AutoLayoutDelegate();
|
||||
|
||||
List<al.Constraint> getConstraints(AutoLayoutParams parentParams);
|
||||
bool shouldUpdateConstraints(AutoLayoutDelegate oldDelegate);
|
||||
}
|
||||
|
||||
class RenderAutoLayout extends RenderBox
|
||||
with ContainerRenderObjectMixin<RenderBox, AutoLayoutParentData>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, AutoLayoutParentData>,
|
||||
_AutoLayoutParamMixin {
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, AutoLayoutParentData> {
|
||||
|
||||
RenderAutoLayout({
|
||||
AutoLayoutDelegate delegate,
|
||||
List<RenderBox> children
|
||||
}) : _delegate = delegate, _needToUpdateConstraints = (delegate != null) {
|
||||
_solver.addEditVariables(<al.Variable>[
|
||||
_params._leftEdge.variable,
|
||||
_params._rightEdge.variable,
|
||||
_params._topEdge.variable,
|
||||
_params._bottomEdge.variable
|
||||
], al.Priority.required - 1);
|
||||
|
||||
RenderAutoLayout({ List<RenderBox> children }) {
|
||||
_setupLayoutParameters(this);
|
||||
_setupEditVariablesInSolver(_solver, al.Priority.required - 1);
|
||||
addAll(children);
|
||||
}
|
||||
|
||||
AutoLayoutDelegate get delegate => _delegate;
|
||||
AutoLayoutDelegate _delegate;
|
||||
void set delegate(AutoLayoutDelegate newDelegate) {
|
||||
if (_delegate == newDelegate)
|
||||
return;
|
||||
AutoLayoutDelegate oldDelegate = _delegate;
|
||||
_delegate = newDelegate;
|
||||
if (newDelegate == null) {
|
||||
assert(oldDelegate != null);
|
||||
_needToUpdateConstraints = true;
|
||||
markNeedsLayout();
|
||||
} else if (oldDelegate == null ||
|
||||
newDelegate.runtimeType != oldDelegate.runtimeType ||
|
||||
newDelegate.shouldUpdateConstraints(oldDelegate)) {
|
||||
_needToUpdateConstraints = true;
|
||||
markNeedsLayout();
|
||||
}
|
||||
}
|
||||
|
||||
bool _needToUpdateConstraints;
|
||||
|
||||
final AutoLayoutParams _params = new AutoLayoutParams();
|
||||
|
||||
final al.Solver _solver = new al.Solver();
|
||||
List<al.Constraint> _explicitConstraints = new List<al.Constraint>();
|
||||
final List<al.Constraint> _explicitConstraints = new List<al.Constraint>();
|
||||
|
||||
/// Adds all the given constraints to the solver. Either all constraints are
|
||||
/// added or none.
|
||||
al.Result addConstraints(List<al.Constraint> constraints) {
|
||||
al.Result result = _solver.addConstraints(constraints);
|
||||
if (result == al.Result.success) {
|
||||
markNeedsLayout();
|
||||
void _addExplicitConstraints(List<al.Constraint> constraints) {
|
||||
if (constraints == null || constraints.isEmpty)
|
||||
return;
|
||||
if (_solver.addConstraints(constraints) == al.Result.success)
|
||||
_explicitConstraints.addAll(constraints);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Adds the given constraint to the solver.
|
||||
al.Result addConstraint(al.Constraint constraint) {
|
||||
al.Result result = _solver.addConstraint(constraint);
|
||||
|
||||
if (result == al.Result.success) {
|
||||
markNeedsLayout();
|
||||
_explicitConstraints.add(constraint);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Removes all explicitly added constraints.
|
||||
al.Result clearAllConstraints() {
|
||||
al.Result result = _solver.removeConstraints(_explicitConstraints);
|
||||
|
||||
if (result == al.Result.success) {
|
||||
markNeedsLayout();
|
||||
_explicitConstraints = new List<al.Constraint>();
|
||||
}
|
||||
|
||||
return result;
|
||||
void _clearExplicitConstraints() {
|
||||
if (_solver.removeConstraints(_explicitConstraints) == al.Result.success)
|
||||
_explicitConstraints.clear();
|
||||
}
|
||||
|
||||
void adoptChild(RenderObject child) {
|
||||
// Make sure to call super first to setup the parent data
|
||||
super.adoptChild(child);
|
||||
final AutoLayoutParentData childParentData = child.parentData;
|
||||
childParentData._setupImplicitConstraints(_solver);
|
||||
childParentData._params?._addImplicitConstraints();
|
||||
assert(child.parentData == childParentData);
|
||||
}
|
||||
|
||||
void dropChild(RenderObject child) {
|
||||
final AutoLayoutParentData childParentData = child.parentData;
|
||||
childParentData._removeImplicitConstraints(_solver);
|
||||
childParentData._params?._removeImplicitConstraints();
|
||||
assert(child.parentData == childParentData);
|
||||
super.dropChild(child);
|
||||
}
|
||||
@ -201,23 +201,41 @@ class RenderAutoLayout extends RenderBox
|
||||
}
|
||||
|
||||
void performLayout() {
|
||||
// Step 1: Update dimensions of self
|
||||
_applyEditsAtSize(_solver, size);
|
||||
// Step 1: Update constraints if needed.
|
||||
if (_needToUpdateConstraints) {
|
||||
_clearExplicitConstraints();
|
||||
if (_delegate != null)
|
||||
_addExplicitConstraints(_delegate.getConstraints(_params));
|
||||
_needToUpdateConstraints = false;
|
||||
}
|
||||
|
||||
// Step 2: Resolve solver updates and flush parameters
|
||||
// Step 2: Update dimensions of this render object.
|
||||
_solver
|
||||
..suggestValueForVariable(_params._leftEdge.variable, 0.0)
|
||||
..suggestValueForVariable(_params._topEdge.variable, 0.0)
|
||||
..suggestValueForVariable(_params._bottomEdge.variable, size.height)
|
||||
..suggestValueForVariable(_params._rightEdge.variable, size.width);
|
||||
|
||||
// Step 3: Resolve solver updates and flush parameters
|
||||
|
||||
// We don't iterate over the children, instead, we ask the solver to tell
|
||||
// us the updated parameters. Attached to the parameters (via the context)
|
||||
// are the _AutoLayoutParamMixin instances.
|
||||
for (_AutoLayoutParamMixin update in _solver.flushUpdates()) {
|
||||
update._applyAutolayoutParameterUpdates();
|
||||
// are the AutoLayoutParams instances.
|
||||
for (AutoLayoutParams update in _solver.flushUpdates()) {
|
||||
RenderBox child = update._renderBox;
|
||||
if (child != null)
|
||||
_layoutChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
void _applyAutolayoutParameterUpdates() {
|
||||
// Nothing to do since the size update has already been presented to the
|
||||
// solver as an edit variable modification. The invokation of this method
|
||||
// only indicates that the value has been flushed to the variable.
|
||||
void _layoutChild(RenderBox child) {
|
||||
assert(debugDoingThisLayout);
|
||||
assert(child.parent == this);
|
||||
final AutoLayoutParentData childParentData = child.parentData;
|
||||
child.layout(childParentData._constraints);
|
||||
childParentData.offset = new Offset(childParentData._params._leftEdge.value,
|
||||
childParentData._params._topEdge.value);
|
||||
assert(child.parentData == childParentData);
|
||||
}
|
||||
|
||||
bool hitTestChildren(HitTestResult result, { Point position }) {
|
||||
@ -227,11 +245,4 @@ class RenderAutoLayout extends RenderBox
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
defaultPaint(context, offset);
|
||||
}
|
||||
|
||||
List<al.Constraint> _constructImplicitConstraints() {
|
||||
// Only edits variables are present on layout containers. If, in the future,
|
||||
// implicit constraints (for say margins, padding, etc.) need to be added,
|
||||
// they must be returned from here.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
42
packages/flutter/lib/src/widgets/auto_layout.dart
Normal file
42
packages/flutter/lib/src/widgets/auto_layout.dart
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
import 'framework.dart';
|
||||
|
||||
export 'package:flutter/rendering.dart' show
|
||||
AutoLayoutParams,
|
||||
AutoLayoutDelegate;
|
||||
|
||||
class AutoLayout extends MultiChildRenderObjectWidget {
|
||||
AutoLayout({
|
||||
Key key,
|
||||
this.delegate,
|
||||
List<Widget> children: const <Widget>[]
|
||||
}) : super(key: key, children: children);
|
||||
|
||||
final AutoLayoutDelegate delegate;
|
||||
|
||||
RenderAutoLayout createRenderObject() => new RenderAutoLayout(delegate: delegate);
|
||||
|
||||
void updateRenderObject(RenderAutoLayout renderObject, AutoLayout oldWidget) {
|
||||
renderObject.delegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
class AutoLayoutChild extends ParentDataWidget<AutoLayout> {
|
||||
AutoLayoutChild({ Key key, this.params, Widget child })
|
||||
: super(key: key, child: child);
|
||||
|
||||
final AutoLayoutParams params;
|
||||
|
||||
void applyParentData(RenderObject renderObject) {
|
||||
assert(renderObject.parentData is AutoLayoutParentData);
|
||||
final AutoLayoutParentData parentData = renderObject.parentData;
|
||||
// AutoLayoutParentData filters out redundant writes and marks needs layout
|
||||
// as appropriate.
|
||||
parentData.params = params;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
library widgets;
|
||||
|
||||
export 'src/widgets/asset_vendor.dart';
|
||||
export 'src/widgets/auto_layout.dart';
|
||||
export 'src/widgets/basic.dart';
|
||||
export 'src/widgets/binding.dart';
|
||||
export 'src/widgets/checked_mode_banner.dart';
|
||||
|
Loading…
Reference in New Issue
Block a user