Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions sky/packages/sky/example/stocks/lib/stock_home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class StockHome extends StatefulComponent {
bool _isSearching = false;
String _searchQuery;

SnackBarStatus _snackBarStatus = SnackBarStatus.inactive;
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
bool _isSnackBarShowing = false;

void _handleSearchBegin() {
Expand Down Expand Up @@ -59,28 +59,28 @@ class StockHome extends StatefulComponent {
}

bool _drawerShowing = false;
DrawerStatus _drawerStatus = DrawerStatus.inactive;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;

void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = DrawerStatus.active;
_drawerStatus = AnimationStatus.forward;
});
}

void _handleDrawerStatusChange(DrawerStatus status) {
void _handleDrawerStatusChange(AnimationStatus status) {
setState(() {
_drawerStatus = status;
});
}

bool _menuShowing = false;
PopupMenuStatus _menuStatus = PopupMenuStatus.inactive;
AnimationStatus _menuStatus = AnimationStatus.dismissed;

void _handleMenuShow() {
setState(() {
_menuShowing = true;
_menuStatus = PopupMenuStatus.active;
_menuStatus = AnimationStatus.forward;
});
}

Expand All @@ -90,7 +90,7 @@ class StockHome extends StatefulComponent {
});
}

void _handleMenuStatusChanged(PopupMenuStatus status) {
void _handleMenuStatusChanged(AnimationStatus status) {
setState(() {
_menuStatus = status;
});
Expand All @@ -112,7 +112,7 @@ class StockHome extends StatefulComponent {
}

Drawer buildDrawer() {
if (_drawerStatus == DrawerStatus.inactive)
if (_drawerStatus == AnimationStatus.dismissed)
return null;
assert(_drawerShowing); // TODO(mpcomplete): this is always true
return new Drawer(
Expand Down Expand Up @@ -246,7 +246,7 @@ class StockHome extends StatefulComponent {
}

Widget buildSnackBar() {
if (_snackBarStatus == SnackBarStatus.inactive)
if (_snackBarStatus == AnimationStatus.dismissed)
return null;
return new SnackBar(
showing: _isSnackBarShowing,
Expand All @@ -259,7 +259,7 @@ class StockHome extends StatefulComponent {
void _handleStockPurchased() {
setState(() {
_isSnackBarShowing = true;
_snackBarStatus = SnackBarStatus.active;
_snackBarStatus = AnimationStatus.forward;
});
}

Expand All @@ -272,7 +272,7 @@ class StockHome extends StatefulComponent {
}

void addMenuToOverlays(List<Widget> overlays) {
if (_menuStatus == PopupMenuStatus.inactive)
if (_menuStatus == AnimationStatus.dismissed)
return;
overlays.add(new ModalOverlay(
children: [new StockMenu(
Expand Down
68 changes: 62 additions & 6 deletions sky/packages/sky/lib/animation/animation_performance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/forces.dart';
import 'package:sky/animation/timeline.dart';

enum AnimationDirection {
forward,
reverse
}

enum AnimationStatus {
dismissed, // stoped at 0
forward, // animating from 0 => 1
reverse, // animating from 1 => 0
completed, // stopped at 1
}

// This class manages a "performance" - a collection of values that change
// based on a timeline. For example, a performance may handle an animation
// of a menu opening by sliding and fading in (changing Y value and opacity)
Expand All @@ -16,30 +28,49 @@ import 'package:sky/animation/timeline.dart';
// manipulating |progress|, or |fling| the timeline causing a physics-based
// simulation to take over the progression.
class AnimationPerformance {
AnimationPerformance() {
AnimationPerformance({this.variable, this.duration}) {
_timeline = new Timeline(_tick);
}

AnimatedVariable variable;
// TODO(mpcomplete): duration should be on a director.
Duration duration;

// Advances from 0 to 1. On each tick, we'll update our variable's values.
Timeline _timeline;
Timeline get timeline => _timeline;

AnimationDirection _direction;
AnimationDirection get direction => _direction;

double get progress => timeline.value;
void set progress(double t) {
// TODO(mpcomplete): should this affect |direction|?
stop();
timeline.value = t.clamp(0.0, 1.0);
_checkStatusChanged();
}

bool get isDismissed => progress == 0.0;
bool get isCompleted => progress == 1.0;
bool get isDismissed => status == AnimationStatus.dismissed;
bool get isCompleted => status == AnimationStatus.completed;
bool get isAnimating => timeline.isAnimating;

Future play() => _animateTo(1.0);
Future reverse() => _animateTo(0.0);
AnimationStatus get status {
if (!isAnimating && progress == 1.0)
return AnimationStatus.completed;
if (!isAnimating && progress == 0.0)
return AnimationStatus.dismissed;
return direction == AnimationDirection.forward ?
AnimationStatus.forward :
AnimationStatus.reverse;
}

Future play([AnimationDirection direction = AnimationDirection.forward]) {
_direction = direction;
return resume();
}
Future forward() => play(AnimationDirection.forward);
Future reverse() => play(AnimationDirection.reverse);
Future resume() => _animateTo(direction == AnimationDirection.forward ? 1.0 : 0.0);

void stop() {
timeline.stop();
Expand All @@ -51,6 +82,9 @@ class AnimationPerformance {
Future fling({double velocity: 1.0, Force force}) {
if (force == null)
force = kDefaultSpringForce;
// This is an approximation - the force may not necessarily result in
// animating the same direction as the initial velocity.
_direction = velocity >= 0.0 ? AnimationDirection.forward : AnimationDirection.reverse;
return timeline.fling(force.release(progress, velocity));
}

Expand All @@ -70,6 +104,27 @@ class AnimationPerformance {
listener();
}

final List<Function> _statusListeners = new List<Function>();

void addStatusListener(Function listener) {
_statusListeners.add(listener);
}

void removeStatusListener(Function listener) {
_statusListeners.remove(listener);
}

AnimationStatus _lastStatus = AnimationStatus.dismissed;
void _checkStatusChanged() {
AnimationStatus currentStatus = status;
if (currentStatus != _lastStatus) {
List<Function> localListeners = new List<Function>.from(_statusListeners);
for (Function listener in localListeners)
listener(currentStatus);
}
_lastStatus = currentStatus;
}

Future _animateTo(double target) {
double remainingDistance = (target - timeline.value).abs();
timeline.stop();
Expand All @@ -81,5 +136,6 @@ class AnimationPerformance {
void _tick(double t) {
variable.setProgress(t);
_notifyListeners();
_checkStatusChanged();
}
}
35 changes: 12 additions & 23 deletions sky/packages/sky/lib/widgets/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import 'package:sky/widgets/scrollable_viewport.dart';
import 'package:sky/widgets/theme.dart';
import 'package:vector_math/vector_math.dart';

export 'package:sky/animation/animation_performance.dart' show AnimationStatus;

// TODO(eseidel): Draw width should vary based on device size:
// http://www.google.com/design/spec/layout/structure.html#structure-side-nav

Expand All @@ -35,14 +37,7 @@ const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
const Point _kOpenPosition = Point.origin;
const Point _kClosedPosition = const Point(-_kWidth, 0.0);

typedef void DrawerStatusChangeHandler (bool showing);

enum DrawerStatus {
active,
inactive,
}

typedef void DrawerStatusChangedCallback(DrawerStatus status);
typedef void DrawerStatusChangedCallback(AnimationStatus status);

class Drawer extends AnimatedComponent {
Drawer({
Expand Down Expand Up @@ -70,7 +65,7 @@ class Drawer extends AnimatedComponent {
_performance = new AnimationPerformance()
..duration = _kBaseSettleDuration
..variable = new AnimatedList([_position, _maskColor])
..addListener(_checkForStateChanged);
..addStatusListener(_onStatusChanged);
watch(_performance);
if (showing)
_show();
Expand Down Expand Up @@ -137,22 +132,16 @@ class Drawer extends AnimatedComponent {

double get xPosition => _position.value.x;

DrawerStatus _lastStatus;
void _checkForStateChanged() {
DrawerStatus status = _status;
if (_lastStatus != null && status != _lastStatus) {
if (status == DrawerStatus.inactive &&
navigator != null &&
navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop();
if (onStatusChanged != null)
onStatusChanged(status);
}
_lastStatus = status;
void _onStatusChanged(AnimationStatus status) {
if (status == AnimationStatus.dismissed &&
navigator != null &&
navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop();
if (onStatusChanged != null)
onStatusChanged(status);
}

DrawerStatus get _status => _performance.isDismissed ? DrawerStatus.inactive : DrawerStatus.active;
bool get _isMostlyClosed => xPosition <= -_kWidth/2;

void _settle() => _fling(_isMostlyClosed ? -1.0 : 1.0);
Expand Down
34 changes: 12 additions & 22 deletions sky/packages/sky/lib/widgets/popup_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import 'package:sky/widgets/navigator.dart';
import 'package:sky/widgets/popup_menu_item.dart';
import 'package:sky/widgets/scrollable_viewport.dart';

export 'package:sky/animation/animation_performance.dart' show AnimationStatus;

const Duration _kMenuDuration = const Duration(milliseconds: 300);
double _kMenuCloseIntervalEnd = 2.0 / 3.0;
const double _kMenuWidthStep = 56.0;
Expand All @@ -25,12 +27,7 @@ const double _kMenuMaxWidth = 5.0 * _kMenuWidthStep;
const double _kMenuHorizontalPadding = 16.0;
const double _kMenuVerticalPadding = 8.0;

enum PopupMenuStatus {
active,
inactive,
}

typedef void PopupMenuStatusChangedCallback(PopupMenuStatus status);
typedef void PopupMenuStatusChangedCallback(AnimationStatus status);

class PopupMenu extends AnimatedComponent {

Expand Down Expand Up @@ -59,7 +56,7 @@ class PopupMenu extends AnimatedComponent {
void initState() {
_performance = new AnimationPerformance()
..duration = _kMenuDuration
..addListener(_checkForStateChanged);
..addStatusListener(_onStatusChanged);
_updateAnimationVariables();
watch(_performance);
_updateBoxPainter();
Expand Down Expand Up @@ -115,21 +112,14 @@ class PopupMenu extends AnimatedComponent {
boxShadow: shadows[level]));
}

PopupMenuStatus get _status => _opacity.value != 0.0 ? PopupMenuStatus.active : PopupMenuStatus.inactive;

PopupMenuStatus _lastStatus;
void _checkForStateChanged() {
PopupMenuStatus status = _status;
if (_lastStatus != null && status != _lastStatus) {
if (status == PopupMenuStatus.inactive &&
navigator != null &&
navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop();
if (onStatusChanged != null)
onStatusChanged(status);
}
_lastStatus = status;
void _onStatusChanged(AnimationStatus status) {
if (status == AnimationStatus.dismissed &&
navigator != null &&
navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop();
if (onStatusChanged != null)
onStatusChanged(status);
}


Expand Down
Loading