2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
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' ;
5
9
import 'package:sky/widgets/basic.dart' ;
10
+ import 'package:vector_math/vector_math.dart' ;
6
11
7
12
typedef Widget Builder (Navigator navigator, RouteBase route);
8
13
@@ -27,31 +32,125 @@ class RouteState extends RouteBase {
27
32
RouteBase route;
28
33
Function callback;
29
34
30
- Widget build (Navigator navigator, _ ) => route. build (navigator, this ) ;
35
+ Widget build (Navigator navigator, RouteBase route ) => null ;
31
36
32
37
void popState () {
33
38
if (callback != null )
34
39
callback (this );
35
40
}
36
41
}
37
42
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
+
38
138
class NavigationState {
39
139
40
140
NavigationState (List <Route > routes) {
41
141
for (Route route in routes) {
42
142
if (route.name != null )
43
143
namedRoutes[route.name] = route;
44
144
}
45
- history.add (routes[0 ]);
145
+ history.add (new HistoryEntry ( routes[0 ]) );
46
146
}
47
147
48
- List <RouteBase > history = new List <RouteBase >();
148
+ List <HistoryEntry > history = new List <HistoryEntry >();
49
149
int historyIndex = 0 ;
50
150
Map <String , RouteBase > namedRoutes = new Map <String , RouteBase >();
51
151
52
- RouteBase get currentRoute => history[historyIndex];
152
+ RouteBase get currentRoute => history[historyIndex].route ;
53
153
bool hasPrevious () => historyIndex > 0 ;
54
- bool hasNext () => history.length > historyIndex + 1 ;
55
154
56
155
void pushNamed (String name) {
57
156
Route route = namedRoutes[name];
@@ -60,16 +159,15 @@ class NavigationState {
60
159
}
61
160
62
161
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++ ;
67
165
}
68
166
69
167
void pop () {
70
168
if (historyIndex > 0 ) {
71
- history[historyIndex]. popState () ;
72
- history. removeLast ();
169
+ HistoryEntry entry = history[historyIndex];
170
+ entry.route. popState ();
73
171
historyIndex-- ;
74
172
}
75
173
}
@@ -115,6 +213,31 @@ class Navigator extends StatefulComponent {
115
213
}
116
214
117
215
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);
119
242
}
120
243
}
0 commit comments