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> { ...@@ -236,7 +236,6 @@ class StockHomeState extends State<StockHome> {
if (_snackBarStatus == AnimationStatus.dismissed) if (_snackBarStatus == AnimationStatus.dismissed)
return null; return null;
return new SnackBar( return new SnackBar(
transitionKey: snackBarKey,
showing: _isSnackBarShowing, showing: _isSnackBarShowing,
content: new Text("Stock purchased!"), content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)], actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
......
...@@ -211,7 +211,9 @@ class SizedBox extends OneChildRenderObjectWidget { ...@@ -211,7 +211,9 @@ class SizedBox extends OneChildRenderObjectWidget {
final double width; final double width;
final double height; final double height;
RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(additionalConstraints: _additionalConstraints); RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(
additionalConstraints: _additionalConstraints
);
BoxConstraints get _additionalConstraints { BoxConstraints get _additionalConstraints {
BoxConstraints result = const BoxConstraints(); BoxConstraints result = const BoxConstraints();
...@@ -227,6 +229,24 @@ class SizedBox extends OneChildRenderObjectWidget { ...@@ -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 { class ConstrainedBox extends OneChildRenderObjectWidget {
ConstrainedBox({ Key key, this.constraints, Widget child }) ConstrainedBox({ Key key, this.constraints, Widget child })
: super(key: key, child: child) { : super(key: key, child: child) {
......
...@@ -102,6 +102,7 @@ class RenderScaffold extends RenderBox { ...@@ -102,6 +102,7 @@ class RenderScaffold extends RenderBox {
void performLayout() { void performLayout() {
double bodyHeight = size.height; double bodyHeight = size.height;
double bodyPosition = 0.0; double bodyPosition = 0.0;
double fabOffset = 0.0;
if (_slots[ScaffoldSlots.statusBar] != null) { if (_slots[ScaffoldSlots.statusBar] != null) {
RenderBox statusBar = _slots[ScaffoldSlots.statusBar]; RenderBox statusBar = _slots[ScaffoldSlots.statusBar];
statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight))); statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight)));
...@@ -127,18 +128,20 @@ class RenderScaffold extends RenderBox { ...@@ -127,18 +128,20 @@ class RenderScaffold extends RenderBox {
if (_slots[ScaffoldSlots.snackBar] != null) { if (_slots[ScaffoldSlots.snackBar] != null) {
RenderBox snackBar = _slots[ScaffoldSlots.snackBar]; RenderBox snackBar = _slots[ScaffoldSlots.snackBar];
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568 // 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), snackBar.layout(
parentUsesSize: true); new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: bodyHeight),
parentUsesSize: true
);
assert(snackBar.parentData is BoxParentData); assert(snackBar.parentData is BoxParentData);
// Position it off-screen. SnackBar slides in with an animation. snackBar.parentData.position = new Point(0.0, bodyPosition + bodyHeight - snackBar.size.height);
snackBar.parentData.position = new Point(0.0, size.height); fabOffset += snackBar.size.height;
} }
if (_slots[ScaffoldSlots.floatingActionButton] != null) { if (_slots[ScaffoldSlots.floatingActionButton] != null) {
RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton]; RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton];
Size area = new Size(size.width - kButtonX, size.height - kButtonY); Size area = new Size(size.width - kButtonX, size.height - kButtonY);
floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true); floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true);
assert(floatingActionButton.parentData is BoxParentData); 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) { if (_slots[ScaffoldSlots.drawer] != null) {
RenderBox drawer = _slots[ScaffoldSlots.drawer]; RenderBox drawer = _slots[ScaffoldSlots.drawer];
......
...@@ -17,7 +17,10 @@ import 'package:sky/src/fn3/transitions.dart'; ...@@ -17,7 +17,10 @@ import 'package:sky/src/fn3/transitions.dart';
typedef void SnackBarDismissedCallback(); typedef void SnackBarDismissedCallback();
const Duration _kSlideInDuration = const Duration(milliseconds: 200); 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 { class SnackBarAction extends StatelessComponent {
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) { SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
...@@ -31,8 +34,8 @@ class SnackBarAction extends StatelessComponent { ...@@ -31,8 +34,8 @@ class SnackBarAction extends StatelessComponent {
return new GestureDetector( return new GestureDetector(
onTap: onPressed, onTap: onPressed,
child: new Container( child: new Container(
margin: const EdgeDims.only(left: 24.0), margin: const EdgeDims.only(left: kSideMargins),
padding: const EdgeDims.only(top: 14.0, bottom: 14.0), padding: const EdgeDims.symmetric(vertical: kVerticalPadding),
child: new Text(label) child: new Text(label)
) )
); );
...@@ -42,7 +45,6 @@ class SnackBarAction extends StatelessComponent { ...@@ -42,7 +45,6 @@ class SnackBarAction extends StatelessComponent {
class SnackBar extends AnimatedComponent { class SnackBar extends AnimatedComponent {
SnackBar({ SnackBar({
Key key, Key key,
this.transitionKey,
this.content, this.content,
this.actions, this.actions,
bool showing, bool showing,
...@@ -51,7 +53,6 @@ class SnackBar extends AnimatedComponent { ...@@ -51,7 +53,6 @@ class SnackBar extends AnimatedComponent {
assert(content != null); assert(content != null);
} }
final Key transitionKey;
final Widget content; final Widget content;
final List<SnackBarAction> actions; final List<SnackBarAction> actions;
final SnackBarDismissedCallback onDismissed; final SnackBarDismissedCallback onDismissed;
...@@ -69,7 +70,7 @@ class SnackBarState extends AnimatedState<SnackBar> { ...@@ -69,7 +70,7 @@ class SnackBarState extends AnimatedState<SnackBar> {
List<Widget> children = [ List<Widget> children = [
new Flexible( new Flexible(
child: new Container( child: new Container(
margin: const EdgeDims.symmetric(vertical: 14.0), margin: const EdgeDims.symmetric(vertical: kVerticalPadding),
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: Typography.white.subhead, style: Typography.white.subhead,
child: config.content child: config.content
...@@ -79,24 +80,28 @@ class SnackBarState extends AnimatedState<SnackBar> { ...@@ -79,24 +80,28 @@ class SnackBarState extends AnimatedState<SnackBar> {
]; ];
if (config.actions != null) if (config.actions != null)
children.addAll(config.actions); children.addAll(config.actions);
return new SlideTransition( return new SquashTransition(
key: config.transitionKey,
performance: performance.view, performance: performance.view,
position: new AnimatedValue<Point>( height: new AnimatedValue<double>(
Point.origin, 0.0,
end: const Point(0.0, -52.0), end: kSnackHeight,
curve: easeIn, curve: easeIn,
reverseCurve: easeOut reverseCurve: easeOut
), ),
child: new Material( child: new ClipRect(
level: 2, child: new OverflowBox(
color: const Color(0xFF323232), height: kSnackHeight,
type: MaterialType.canvas, child: new Material(
child: new Container( level: 2,
margin: const EdgeDims.symmetric(horizontal: 24.0), color: kSnackBackground,
child: new DefaultTextStyle( type: MaterialType.canvas,
style: new TextStyle(color: Theme.of(context).accentColor), child: new Container(
child: new Row(children) 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 ...@@ -83,7 +83,7 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
/// A render object that imposes additional constraints on its child /// 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 /// to its child, except that when laying out its child, it tightens the
/// constraints provided by its parent by enforcing the [additionalConstraints] /// constraints provided by its parent by enforcing the [additionalConstraints]
/// as well. /// as well.
...@@ -94,7 +94,7 @@ class RenderConstrainedBox extends RenderProxyBox { ...@@ -94,7 +94,7 @@ class RenderConstrainedBox extends RenderProxyBox {
RenderConstrainedBox({ RenderConstrainedBox({
RenderBox child, RenderBox child,
BoxConstraints additionalConstraints BoxConstraints additionalConstraints
}) : super(child), _additionalConstraints = additionalConstraints { }) : _additionalConstraints = additionalConstraints, super(child) {
assert(additionalConstraints != null); assert(additionalConstraints != null);
} }
...@@ -145,6 +145,99 @@ class RenderConstrainedBox extends RenderProxyBox { ...@@ -145,6 +145,99 @@ class RenderConstrainedBox extends RenderProxyBox {
String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}additionalConstraints: ${additionalConstraints}\n'; 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 /// Forces child to layout at a specific aspect ratio
/// ///
/// The width of this render object is the largest width permited by the layout /// 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