提交 dee68441 编写于 作者: M mpcomplete

Merge pull request #564 from mpcomplete/anchor

Scaffold: animate the FloatingActionButton with the SnackBar.
......@@ -80,6 +80,7 @@ class FeedFragment extends StatefulComponent {
onItemDeleted = source.onItemDeleted;
}
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
bool _isShowingSnackBar = false;
EventDisposition _handleFitnessModeChange(FitnessMode value) {
......@@ -168,6 +169,7 @@ class FeedFragment extends StatefulComponent {
setState(() {
_undoItem = item;
_isShowingSnackBar = true;
_snackBarStatus = AnimationStatus.forward;
});
}
......@@ -205,13 +207,16 @@ class FeedFragment extends StatefulComponent {
});
}
Anchor _snackBarAnchor = new Anchor();
Widget buildSnackBar() {
if (!_isShowingSnackBar)
if (_snackBarStatus == AnimationStatus.dismissed)
return null;
return new SnackBar(
showing: _isShowingSnackBar,
anchor: _snackBarAnchor,
content: new Text("Item deleted."),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)]
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
);
}
......@@ -225,10 +230,11 @@ class FeedFragment extends StatefulComponent {
Widget buildFloatingActionButton() {
switch (_fitnessMode) {
case FitnessMode.feed:
return new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
onPressed: _handleActionButtonPressed
);
return _snackBarAnchor.build(
new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
onPressed: _handleActionButtonPressed
));
case FitnessMode.chart:
return null;
}
......
......@@ -247,11 +247,13 @@ class StockHome extends StatefulComponent {
});
}
Anchor _snackBarAnchor = new Anchor();
Widget buildSnackBar() {
if (_snackBarStatus == AnimationStatus.dismissed)
return null;
return new SnackBar(
showing: _isSnackBarShowing,
anchor: _snackBarAnchor,
content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
......@@ -266,11 +268,12 @@ class StockHome extends StatefulComponent {
}
Widget buildFloatingActionButton() {
return new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
backgroundColor: colors.RedAccent[200],
onPressed: _handleStockPurchased
);
return _snackBarAnchor.build(
new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
backgroundColor: colors.RedAccent[200],
onPressed: _handleStockPurchased
));
}
void addMenuToOverlays(List<Widget> overlays) {
......
......@@ -20,13 +20,22 @@ abstract class AnimatedComponent extends StatefulComponent {
});
}
bool isWatching(performance) => _watchedPerformances.contains(performance);
void watch(AnimationPerformance performance) {
assert(!_watchedPerformances.contains(performance));
assert(!isWatching(performance));
_watchedPerformances.add(performance);
if (mounted)
performance.addListener(_performanceChanged);
}
void unwatch(AnimationPerformance performance) {
assert(isWatching(performance));
_watchedPerformances.remove(performance);
if (mounted)
performance.removeListener(_performanceChanged);
}
void didMount() {
for (AnimationPerformance performance in _watchedPerformances)
performance.addListener(_performanceChanged);
......
......@@ -85,7 +85,7 @@ class Transition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
// TODO(jackson): Hit testing should ignore transform
// TODO(jackson): Block input unless content is interactive
return new SlideTransition(
......
......@@ -9,6 +9,8 @@ import 'package:sky/rendering/object.dart';
import 'package:sky/theme/view_configuration.dart';
import 'package:sky/widgets/framework.dart';
import 'package:vector_math/vector_math.dart';
// Slots are painted in this order and hit tested in reverse of this order
enum ScaffoldSlots {
body,
......@@ -117,19 +119,18 @@ class RenderScaffold extends RenderBox {
assert(body.parentData is BoxParentData);
body.parentData.position = new Point(0.0, bodyPosition);
}
double snackBarHeight = 0.0;
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);
assert(snackBar.parentData is BoxParentData);
snackBar.parentData.position = new Point(0.0, size.height - snackBar.size.height);
snackBarHeight = snackBar.size.height;
// Position it off-screen. SnackBar slides in with an animation.
snackBar.parentData.position = new Point(0.0, size.height);
}
if (_slots[ScaffoldSlots.floatingActionButton] != null) {
RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton];
Size area = new Size(size.width - kButtonX, size.height - kButtonY - snackBarHeight);
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();
......@@ -152,12 +153,32 @@ class RenderScaffold extends RenderBox {
}
}
static Point _transformPoint(Matrix4 transform, Point point) {
Vector3 position3 = new Vector3(point.x, point.y, 0.0);
Vector3 transformed3 = transform.transform3(position3);
return new Point(transformed3.x, transformed3.y);
}
Point parentToLocal(RenderBox box, Point point) {
assert(attached);
Matrix4 transform = new Matrix4.identity();
box.applyPaintTransform(transform);
/* double det = */ transform.invert();
// TODO(abarth): Check the determinant for degeneracy.
return _transformPoint(transform, point);
}
void hitTestChildren(HitTestResult result, { Point position }) {
for (ScaffoldSlots slot in ScaffoldSlots.values.reversed) {
RenderBox box = _slots[slot];
if (box != null) {
assert(box.parentData is BoxParentData);
if ((box.parentData.position & box.size).contains(position)) {
// TODO(abarth): Need to solve this problem in general.
// Apply the box's transform to check if it contains position.
// But when we pass the position to box.hitTest, we only want to apply
// the top-level transform (box will apply its own transforms).
Point local = parentToLocal(box, position);
if ((Point.origin & box.size).contains(local)) {
if (box.hitTest(result, position: (position - box.parentData.position).toPoint()))
return;
}
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/painting/text_style.dart';
import 'package:sky/theme/typography.dart' as typography;
import 'package:sky/widgets/basic.dart';
......@@ -43,6 +44,7 @@ class SnackBar extends Component {
SnackBar({
Key key,
this.anchor,
this.content,
this.actions,
this.showing,
......@@ -51,6 +53,7 @@ class SnackBar extends Component {
assert(content != null);
}
Anchor anchor;
Widget content;
List<SnackBarAction> actions;
bool showing;
......@@ -79,9 +82,11 @@ class SnackBar extends Component {
return new SlideTransition(
duration: _kSlideInDuration,
direction: showing ? Direction.forward : Direction.reverse,
position: new AnimatedValue<Point>(const Point(0.0, 50.0),
end: Point.origin),
position: new AnimatedValue<Point>(Point.origin,
end: const Point(0.0, -52.0),
curve: easeIn, reverseCurve: easeOut),
onDismissed: _onDismissed,
anchor: anchor,
child: new Material(
level: 2,
color: const Color(0xFF323232),
......
......@@ -10,9 +10,57 @@ import 'package:vector_math/vector_math.dart';
dynamic _maybe(AnimatedValue x) => x != null ? x.value : null;
// A helper class to anchor widgets to one another. Pass an instance of this to
// a Transition, then use the build() method to create a child with the same
// transition applied.
class Anchor {
Anchor();
TransitionBase transition;
Widget build(Widget child) {
return new _AnchorTransition(anchoredTo: this, child: child);
}
}
// Used with the Anchor class to apply a transition to multiple children.
class _AnchorTransition extends AnimatedComponent {
_AnchorTransition({
Key key,
this.anchoredTo,
this.child
}) : super(key: key);
Anchor anchoredTo;
Widget child;
TransitionBase get transition => anchoredTo.transition;
void initState() {
if (transition != null)
watch(transition.performance);
}
void syncFields(_AnchorTransition source) {
if (transition != null && isWatching(transition.performance))
unwatch(transition.performance);
anchoredTo = source.anchoredTo;
if (transition != null)
watch(transition.performance);
child = source.child;
super.syncFields(source);
}
Widget build() {
if (transition == null)
return child;
return transition.buildWithChild(child);
}
}
abstract class TransitionBase extends AnimatedComponent {
TransitionBase({
Key key,
this.anchor,
this.child,
this.direction,
this.duration,
......@@ -22,6 +70,7 @@ abstract class TransitionBase extends AnimatedComponent {
}) : super(key: key);
Widget child;
Anchor anchor;
Direction direction;
Duration duration;
AnimationPerformance performance;
......@@ -29,6 +78,9 @@ abstract class TransitionBase extends AnimatedComponent {
Function onCompleted;
void initState() {
if (anchor != null)
anchor.transition = this;
if (performance == null) {
assert(duration != null);
performance = new AnimationPerformance(duration: duration);
......@@ -67,7 +119,11 @@ abstract class TransitionBase extends AnimatedComponent {
}
}
Widget build();
Widget build() {
return buildWithChild(child);
}
Widget buildWithChild(Widget child);
}
class SlideTransition extends TransitionBase {
......@@ -75,6 +131,7 @@ class SlideTransition extends TransitionBase {
// to super. Is there a simpler way?
SlideTransition({
Key key,
Anchor anchor,
this.position,
Duration duration,
AnimationPerformance performance,
......@@ -83,6 +140,7 @@ class SlideTransition extends TransitionBase {
Function onCompleted,
Widget child
}) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance,
direction: direction,
......@@ -97,7 +155,7 @@ class SlideTransition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
performance.updateVariable(position);
Matrix4 transform = new Matrix4.identity()
..translate(position.value.x, position.value.y);
......@@ -108,6 +166,7 @@ class SlideTransition extends TransitionBase {
class FadeTransition extends TransitionBase {
FadeTransition({
Key key,
Anchor anchor,
this.opacity,
Duration duration,
AnimationPerformance performance,
......@@ -116,6 +175,7 @@ class FadeTransition extends TransitionBase {
Function onCompleted,
Widget child
}) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance,
direction: direction,
......@@ -130,7 +190,7 @@ class FadeTransition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
performance.updateVariable(opacity);
return new Opacity(opacity: opacity.value, child: child);
}
......@@ -139,6 +199,7 @@ class FadeTransition extends TransitionBase {
class ColorTransition extends TransitionBase {
ColorTransition({
Key key,
Anchor anchor,
this.color,
Duration duration,
AnimationPerformance performance,
......@@ -147,6 +208,7 @@ class ColorTransition extends TransitionBase {
Function onCompleted,
Widget child
}) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance,
direction: direction,
......@@ -161,7 +223,7 @@ class ColorTransition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
performance.updateVariable(color);
return new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: color.value),
......@@ -173,6 +235,7 @@ class ColorTransition extends TransitionBase {
class SquashTransition extends TransitionBase {
SquashTransition({
Key key,
Anchor anchor,
this.width,
this.height,
Duration duration,
......@@ -182,6 +245,7 @@ class SquashTransition extends TransitionBase {
Function onCompleted,
Widget child
}) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance,
direction: direction,
......@@ -198,7 +262,7 @@ class SquashTransition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
if (width != null)
performance.updateVariable(width);
if (height != null)
......@@ -212,6 +276,7 @@ typedef Widget BuilderFunction();
class BuilderTransition extends TransitionBase {
BuilderTransition({
Key key,
Anchor anchor,
this.variables,
this.builder,
Duration duration,
......@@ -221,6 +286,7 @@ class BuilderTransition extends TransitionBase {
Function onCompleted,
Widget child
}) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance,
direction: direction,
......@@ -237,7 +303,7 @@ class BuilderTransition extends TransitionBase {
super.syncFields(source);
}
Widget build() {
Widget buildWithChild(Widget child) {
for (int i = 0; i < variables.length; ++i)
performance.updateVariable(variables[i]);
return builder();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册