Commit 2ed1b608 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #1371 from Hixie/fab-snack

Make the FAB move up when a Snack Bar slides in.
parents a656d57d 56d40334
......@@ -236,7 +236,6 @@ class StockHomeState extends State<StockHome> {
if (_snackBarStatus == AnimationStatus.dismissed)
return null;
return new SnackBar(
transitionKey: snackBarKey,
showing: _isSnackBarShowing,
content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
......
......@@ -211,7 +211,9 @@ class SizedBox extends OneChildRenderObjectWidget {
final double width;
final double height;
RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(additionalConstraints: _additionalConstraints);
RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(
additionalConstraints: _additionalConstraints
);
BoxConstraints get _additionalConstraints {
BoxConstraints result = const BoxConstraints();
......@@ -227,6 +229,24 @@ class SizedBox extends OneChildRenderObjectWidget {
}
}
class OverflowBox extends OneChildRenderObjectWidget {
OverflowBox({ Key key, this.width, this.height, Widget child })
: super(key: key, child: child);
final double width;
final double height;
RenderOverflowBox createRenderObject() => new RenderOverflowBox(
innerWidth: width,
innerHeight: height
);
void updateRenderObject(RenderOverflowBox renderObject, OverflowBox oldWidget) {
renderObject.innerWidth = width;
renderObject.innerHeight = height;
}
}
class ConstrainedBox extends OneChildRenderObjectWidget {
ConstrainedBox({ Key key, this.constraints, Widget child })
: super(key: key, child: child) {
......
......@@ -102,6 +102,7 @@ class RenderScaffold extends RenderBox {
void performLayout() {
double bodyHeight = size.height;
double bodyPosition = 0.0;
double fabOffset = 0.0;
if (_slots[ScaffoldSlots.statusBar] != null) {
RenderBox statusBar = _slots[ScaffoldSlots.statusBar];
statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight)));
......@@ -127,18 +128,20 @@ class RenderScaffold extends RenderBox {
if (_slots[ScaffoldSlots.snackBar] != null) {
RenderBox snackBar = _slots[ScaffoldSlots.snackBar];
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
snackBar.layout(new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: size.height),
parentUsesSize: true);
snackBar.layout(
new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: bodyHeight),
parentUsesSize: true
);
assert(snackBar.parentData is BoxParentData);
// Position it off-screen. SnackBar slides in with an animation.
snackBar.parentData.position = new Point(0.0, size.height);
snackBar.parentData.position = new Point(0.0, bodyPosition + bodyHeight - snackBar.size.height);
fabOffset += snackBar.size.height;
}
if (_slots[ScaffoldSlots.floatingActionButton] != null) {
RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton];
Size area = new Size(size.width - kButtonX, size.height - kButtonY);
floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true);
assert(floatingActionButton.parentData is BoxParentData);
floatingActionButton.parentData.position = (area - floatingActionButton.size).toPoint();
floatingActionButton.parentData.position = (area - floatingActionButton.size).toPoint() + new Offset(0.0, -fabOffset);
}
if (_slots[ScaffoldSlots.drawer] != null) {
RenderBox drawer = _slots[ScaffoldSlots.drawer];
......
......@@ -17,7 +17,10 @@ import 'package:sky/src/fn3/transitions.dart';
typedef void SnackBarDismissedCallback();
const Duration _kSlideInDuration = const Duration(milliseconds: 200);
// TODO(ianh): factor out some of the constants below
const double kSnackHeight = 52.0;
const double kSideMargins = 24.0;
const double kVerticalPadding = 14.0;
const Color kSnackBackground = const Color(0xFF323232);
class SnackBarAction extends StatelessComponent {
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
......@@ -31,8 +34,8 @@ class SnackBarAction extends StatelessComponent {
return new GestureDetector(
onTap: onPressed,
child: new Container(
margin: const EdgeDims.only(left: 24.0),
padding: const EdgeDims.only(top: 14.0, bottom: 14.0),
margin: const EdgeDims.only(left: kSideMargins),
padding: const EdgeDims.symmetric(vertical: kVerticalPadding),
child: new Text(label)
)
);
......@@ -42,7 +45,6 @@ class SnackBarAction extends StatelessComponent {
class SnackBar extends AnimatedComponent {
SnackBar({
Key key,
this.transitionKey,
this.content,
this.actions,
bool showing,
......@@ -51,7 +53,6 @@ class SnackBar extends AnimatedComponent {
assert(content != null);
}
final Key transitionKey;
final Widget content;
final List<SnackBarAction> actions;
final SnackBarDismissedCallback onDismissed;
......@@ -69,7 +70,7 @@ class SnackBarState extends AnimatedState<SnackBar> {
List<Widget> children = [
new Flexible(
child: new Container(
margin: const EdgeDims.symmetric(vertical: 14.0),
margin: const EdgeDims.symmetric(vertical: kVerticalPadding),
child: new DefaultTextStyle(
style: Typography.white.subhead,
child: config.content
......@@ -79,24 +80,28 @@ class SnackBarState extends AnimatedState<SnackBar> {
];
if (config.actions != null)
children.addAll(config.actions);
return new SlideTransition(
key: config.transitionKey,
return new SquashTransition(
performance: performance.view,
position: new AnimatedValue<Point>(
Point.origin,
end: const Point(0.0, -52.0),
height: new AnimatedValue<double>(
0.0,
end: kSnackHeight,
curve: easeIn,
reverseCurve: easeOut
),
child: new Material(
level: 2,
color: const Color(0xFF323232),
type: MaterialType.canvas,
child: new Container(
margin: const EdgeDims.symmetric(horizontal: 24.0),
child: new DefaultTextStyle(
style: new TextStyle(color: Theme.of(context).accentColor),
child: new Row(children)
child: new ClipRect(
child: new OverflowBox(
height: kSnackHeight,
child: new Material(
level: 2,
color: kSnackBackground,
type: MaterialType.canvas,
child: new Container(
margin: const EdgeDims.symmetric(horizontal: kSideMargins),
child: new DefaultTextStyle(
style: new TextStyle(color: Theme.of(context).accentColor),
child: new Row(children)
)
)
)
)
)
......
......@@ -83,7 +83,7 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
/// A render object that imposes additional constraints on its child
///
/// A render constrainted box proxies most functions in the render box protocol
/// A render constrained box proxies most functions in the render box protocol
/// to its child, except that when laying out its child, it tightens the
/// constraints provided by its parent by enforcing the [additionalConstraints]
/// as well.
......@@ -94,7 +94,7 @@ class RenderConstrainedBox extends RenderProxyBox {
RenderConstrainedBox({
RenderBox child,
BoxConstraints additionalConstraints
}) : super(child), _additionalConstraints = additionalConstraints {
}) : _additionalConstraints = additionalConstraints, super(child) {
assert(additionalConstraints != null);
}
......@@ -145,6 +145,99 @@ class RenderConstrainedBox extends RenderProxyBox {
String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}additionalConstraints: ${additionalConstraints}\n';
}
/// A render object that imposes different constraints on its child than it gets
/// from its parent, possibly allowing the child to overflow the parent.
///
/// A render overflow box proxies most functions in the render box protocol to
/// its child, except that when laying out its child, it passes constraints
/// based on the innerWidth and innerHeight fields instead of just passing the
/// parent's constraints in. It then sizes itself based on the parent's
/// constraints' maxWidth and maxHeight, ignoring the child's dimensions.
///
/// For example, if you wanted a box to always render 50x50, regardless of where
/// it was rendered, you would wrap it in a RenderOverflow with innerWidth and
/// innerHeight members set to 50.0. Generally speaking, to avoid confusing
/// behaviour around hit testing, a RenderOverflowBox should usually be wrapped
/// in a RenderClipRect.
///
/// The child is positioned at the top left of the box. To position a smaller
/// child inside a larger parent, use [RenderPositionedBox] and
/// [RenderConstrainedBox] rather than RenderOverflowBox.
///
/// If you pass null for innerWidth or innerHeight, the constraints from the
/// parent are passed instead.
class RenderOverflowBox extends RenderProxyBox {
RenderOverflowBox({
RenderBox child,
double innerWidth,
double innerHeight
}) : _innerWidth = innerWidth, _innerHeight = innerHeight, super(child);
/// The tight width constraint to give the child. Set this to null (the
/// default) to use the constraints from the parent instead.
double get innerWidth => _innerWidth;
double _innerWidth;
void set innerWidth (double value) {
if (_innerWidth == value)
return;
_innerWidth = value;
markNeedsLayout();
}
/// The tight height constraint to give the child. Set this to null (the
/// default) to use the constraints from the parent instead.
double get innerHeight => _innerHeight;
double _innerHeight;
void set innerHeight (double value) {
if (_innerHeight == value)
return;
_innerHeight = value;
markNeedsLayout();
}
BoxConstraints childConstraints(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: _innerWidth ?? constraints.minWidth,
maxWidth: _innerWidth ?? constraints.maxWidth,
minHeight: _innerHeight ?? constraints.minHeight,
maxHeight: _innerHeight ?? constraints.maxHeight
);
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth();
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth();
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight();
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight();
}
bool get sizedByParent => true;
void performResize() {
size = constraints.biggest;
}
void performLayout() {
if (child != null)
child.layout(childConstraints(constraints));
}
String debugDescribeSettings(String prefix) {
return '${super.debugDescribeSettings(prefix)}' +
'${prefix}innerWidth: ${innerWidth ?? "use parent width constraints"}\n' +
'${prefix}innerHeight: ${innerHeight ?? "use parent height constraints"}\n';
}
}
/// Forces child to layout at a specific aspect ratio
///
/// The width of this render object is the largest width permited by the layout
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment