Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 881addf

Browse files
committed
Merge pull request #70 from collinjackson/navigator
Support for settings fly-in animation R=abarth
2 parents 770dae4 + 10d5192 commit 881addf

File tree

2 files changed

+152
-23
lines changed

2 files changed

+152
-23
lines changed

sky/sdk/example/widgets/navigation.dart

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ List<Route> routes = [
1010
new Route(
1111
name: 'home',
1212
builder: (navigator, route) => new Container(
13-
padding: const EdgeDims.all(20.0),
13+
padding: const EdgeDims.all(30.0),
1414
decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)),
15-
child: new Block([
15+
child: new Flex([
1616
new Text("You are at home"),
1717
new RaisedButton(
1818
child: new Text('GO SHOPPING'),
@@ -21,16 +21,18 @@ List<Route> routes = [
2121
new RaisedButton(
2222
child: new Text('START ADVENTURE'),
2323
onPressed: () => navigator.pushNamed('adventure')
24-
)
25-
])
24+
)],
25+
direction: FlexDirection.vertical,
26+
justifyContent: FlexJustifyContent.center
27+
)
2628
)
2729
),
2830
new Route(
2931
name: 'shopping',
3032
builder: (navigator, route) => new Container(
3133
padding: const EdgeDims.all(20.0),
3234
decoration: new BoxDecoration(backgroundColor: const Color(0xFFBF5FFF)),
33-
child: new Block([
35+
child: new Flex([
3436
new Text("Village Shop"),
3537
new RaisedButton(
3638
child: new Text('RETURN HOME'),
@@ -39,22 +41,26 @@ List<Route> routes = [
3941
new RaisedButton(
4042
child: new Text('GO TO DUNGEON'),
4143
onPressed: () => navigator.push(routes[2])
42-
)
43-
])
44+
)],
45+
direction: FlexDirection.vertical,
46+
justifyContent: FlexJustifyContent.center
47+
)
4448
)
4549
),
4650
new Route(
4751
name: 'adventure',
4852
builder: (navigator, route) => new Container(
4953
padding: const EdgeDims.all(20.0),
5054
decoration: new BoxDecoration(backgroundColor: const Color(0xFFDC143C)),
51-
child: new Block([
55+
child: new Flex([
5256
new Text("Monster's Lair"),
5357
new RaisedButton(
54-
child: new Text('NO WAIT! GO BACK!'),
58+
child: new Text('RUN!!!'),
5559
onPressed: () => navigator.pop()
56-
)
57-
])
60+
)],
61+
direction: FlexDirection.vertical,
62+
justifyContent: FlexJustifyContent.center
63+
)
5864
)
5965
)
6066
];

sky/sdk/lib/widgets/navigator.dart

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:sky/animation/animation_performance.dart';
6+
import 'package:sky/animation/curves.dart';
7+
import 'package:sky/widgets/animated_component.dart';
8+
import 'package:sky/widgets/animation_builder.dart';
59
import 'package:sky/widgets/basic.dart';
10+
import 'package:vector_math/vector_math.dart';
611

712
typedef Widget Builder(Navigator navigator, RouteBase route);
813

@@ -27,31 +32,125 @@ class RouteState extends RouteBase {
2732
RouteBase route;
2833
Function callback;
2934

30-
Widget build(Navigator navigator, _) => route.build(navigator, this);
35+
Widget build(Navigator navigator, RouteBase route) => null;
3136

3237
void popState() {
3338
if (callback != null)
3439
callback(this);
3540
}
3641
}
3742

43+
// TODO(jackson): Refactor this into its own file
44+
// and support multiple transition types
45+
const Duration _kTransitionDuration = const Duration(milliseconds: 200);
46+
const Point _kTransitionStartPoint = const Point(0.0, 100.0);
47+
enum TransitionDirection { forward, reverse }
48+
class Transition extends AnimatedComponent {
49+
Transition({
50+
String key,
51+
this.content,
52+
this.direction,
53+
this.onDismissed,
54+
this.interactive
55+
}) : super(key: key);
56+
Widget content;
57+
TransitionDirection direction;
58+
bool interactive;
59+
Function onDismissed;
60+
61+
AnimatedType<Point> _position;
62+
AnimatedType<double> _opacity;
63+
AnimationPerformance _performance;
64+
65+
void initState() {
66+
_position = new AnimatedType<Point>(
67+
_kTransitionStartPoint,
68+
end: Point.origin,
69+
curve: easeOut
70+
);
71+
_opacity = new AnimatedType<double>(0.0, end: 1.0)
72+
..curve = easeOut;
73+
_performance = new AnimationPerformance()
74+
..duration = _kTransitionDuration
75+
..variable = new AnimatedList([_position, _opacity])
76+
..addListener(_checkDismissed);
77+
if (direction == TransitionDirection.reverse)
78+
_performance.progress = 1.0;
79+
watch(_performance);
80+
_start();
81+
}
82+
83+
void _start() {
84+
_dismissed = false;
85+
switch (direction) {
86+
case TransitionDirection.forward:
87+
_performance.play();
88+
break;
89+
case TransitionDirection.reverse:
90+
_performance.reverse();
91+
break;
92+
}
93+
}
94+
95+
void syncFields(Transition source) {
96+
content = source.content;
97+
if (direction != source.direction) {
98+
direction = source.direction;
99+
_start();
100+
}
101+
interactive = source.interactive;
102+
onDismissed = source.onDismissed;
103+
super.syncFields(source);
104+
}
105+
106+
bool _dismissed = false;
107+
void _checkDismissed() {
108+
if (!_dismissed &&
109+
direction == TransitionDirection.reverse &&
110+
_performance.isDismissed) {
111+
if (onDismissed != null)
112+
onDismissed();
113+
_dismissed = true;
114+
}
115+
}
116+
117+
Widget build() {
118+
Matrix4 transform = new Matrix4.identity()
119+
..translate(_position.value.x, _position.value.y);
120+
// TODO(jackson): Hit testing should ignore transform
121+
// TODO(jackson): Block input unless content is interactive
122+
return new Transform(
123+
transform: transform,
124+
child: new Opacity(
125+
opacity: _opacity.value,
126+
child: content
127+
)
128+
);
129+
}
130+
}
131+
132+
class HistoryEntry {
133+
HistoryEntry(this.route);
134+
final RouteBase route;
135+
// TODO(jackson): Keep track of the requested transition
136+
}
137+
38138
class NavigationState {
39139

40140
NavigationState(List<Route> routes) {
41141
for (Route route in routes) {
42142
if (route.name != null)
43143
namedRoutes[route.name] = route;
44144
}
45-
history.add(routes[0]);
145+
history.add(new HistoryEntry(routes[0]));
46146
}
47147

48-
List<RouteBase> history = new List<RouteBase>();
148+
List<HistoryEntry> history = new List<HistoryEntry>();
49149
int historyIndex = 0;
50150
Map<String, RouteBase> namedRoutes = new Map<String, RouteBase>();
51151

52-
RouteBase get currentRoute => history[historyIndex];
152+
RouteBase get currentRoute => history[historyIndex].route;
53153
bool hasPrevious() => historyIndex > 0;
54-
bool hasNext() => history.length > historyIndex + 1;
55154

56155
void pushNamed(String name) {
57156
Route route = namedRoutes[name];
@@ -60,16 +159,15 @@ class NavigationState {
60159
}
61160

62161
void push(RouteBase route) {
63-
// Discard future history
64-
history.removeRange(historyIndex + 1, history.length);
65-
historyIndex = history.length;
66-
history.add(route);
162+
HistoryEntry historyEntry = new HistoryEntry(route);
163+
history.insert(historyIndex + 1, historyEntry);
164+
historyIndex++;
67165
}
68166

69167
void pop() {
70168
if (historyIndex > 0) {
71-
history[historyIndex].popState();
72-
history.removeLast();
169+
HistoryEntry entry = history[historyIndex];
170+
entry.route.popState();
73171
historyIndex--;
74172
}
75173
}
@@ -115,6 +213,31 @@ class Navigator extends StatefulComponent {
115213
}
116214

117215
Widget build() {
118-
return state.currentRoute.build(this, state.currentRoute);
216+
List<Widget> visibleRoutes = new List<Widget>();
217+
for (int i = 0; i < state.history.length; i++) {
218+
// TODO(jackson): Avoid building routes that are not visible
219+
HistoryEntry historyEntry = state.history[i];
220+
Widget content = historyEntry.route.build(this, historyEntry.route);
221+
if (i == 0) {
222+
visibleRoutes.add(content);
223+
continue;
224+
}
225+
if (content == null)
226+
continue;
227+
String key = historyEntry.route.key;
228+
Transition transition = new Transition(
229+
key: key,
230+
content: content,
231+
direction: (i <= state.historyIndex) ? TransitionDirection.forward : TransitionDirection.reverse,
232+
interactive: (i == state.historyIndex),
233+
onDismissed: () {
234+
setState(() {
235+
state.history.remove(historyEntry);
236+
});
237+
}
238+
);
239+
visibleRoutes.add(transition);
240+
}
241+
return new Stack(visibleRoutes);
119242
}
120243
}

0 commit comments

Comments
 (0)