Merge pull request #1371 from Hixie/fab-snack
Make the FAB move up when a Snack Bar slides in.
This commit is contained in:
commit
2ed1b60808
@ -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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
|||||||
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 {
|
|||||||
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';
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
];
|
];
|
||||||
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
|
|||||||
|
|
||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user