@@ -9,6 +9,8 @@ class RoutingKitRoutingService<T, Config extends Object>
99 with LifecycleMixin {
1010 RouteContext ? _context;
1111
12+ final Map <Uri , (Future <bool >, LeafRoute <T , Config >)> _pendingNavigations = {};
13+
1214 /// The root module of the application.
1315 final RootModule <T , Config > rootModule;
1416
@@ -30,8 +32,8 @@ class RoutingKitRoutingService<T, Config extends Object>
3032 RouteContext ? get currentContext => _context;
3133
3234 @override
33- FutureOr <void > dispose () {
34- super .dispose ();
35+ FutureOr <void > free () {
36+ super .free ();
3537 _listeners.clear ();
3638 }
3739
@@ -116,82 +118,145 @@ class RoutingKitRoutingService<T, Config extends Object>
116118 Future <void > navigate (
117119 String path, {
118120 bool skipPreview = false ,
119- required void Function (T ) callback,
121+ required void Function (T , bool ) callback,
120122 }) async {
121123 final uri = Uri .parse (path);
122124
123125 if (uri == currentContext? .uri) {
124126 log ('Already at path: $path , skipping navigation.' );
127+
125128 return ;
126129 }
127130
128- final cleanPath = uri.path;
131+ if (_pendingNavigations.containsKey (uri)) {
132+ log ('Navigation to $path is already in progress, forwarding callback.' );
129133
130- // find the route
131- final match = _kit.find (null , cleanPath);
134+ final (future, leaf) = _pendingNavigations[uri]! ;
132135
133- if (match == null ) {
134- throw ArgumentError .value (
135- path,
136- 'path' ,
137- 'No route found for the given path!' ,
138- );
139- }
136+ if (! skipPreview) {
137+ callback (leaf.view.preview (RouteContext .fromUri (uri)), true );
138+ }
140139
141- var leaf = match.data ;
140+ log ( 'Waiting for pending navigation to $ path to complete.' ) ;
142141
143- if (leaf is ModuleRoute <T , Config >) {
144- leaf =
145- leaf.root ??
146- (throw ArgumentError .value (
147- path,
148- 'path' ,
149- 'Resolved ModuleRoute does not have a root LeafRoute defined!' ,
150- ));
151- }
142+ final success = await future;
143+
144+ if (! success) {
145+ log (
146+ 'Pending navigation to $path failed, not invoking content callback.' ,
147+ );
148+ return ;
149+ }
152150
153- // check if leaf (throw if not)
154- if (leaf is ! LeafRoute ) {
155- throw ArgumentError .value (path, 'path' , 'Resolved route is not a leaf!' );
151+ log ('Pending navigation to $path completed, invoking content callback.' );
152+
153+ callback (await leaf.view.content (RouteContext .fromUri (uri)), false );
154+
155+ return ;
156156 }
157157
158- leaf as LeafRoute <T , Config >;
158+ try {
159+ final cleanPath = uri.path;
160+
161+ // find the route
162+ final match = _kit.find (null , cleanPath);
163+
164+ if (match == null ) {
165+ throw ArgumentError .value (
166+ path,
167+ 'path' ,
168+ 'No route found for the given path!' ,
169+ );
170+ }
171+
172+ var leaf = match.data;
159173
160- // find context
174+ if (leaf is ModuleRoute <T , Config >) {
175+ leaf =
176+ leaf.root ??
177+ (throw ArgumentError .value (
178+ path,
179+ 'path' ,
180+ 'Resolved ModuleRoute does not have a root LeafRoute defined!' ,
181+ ));
182+ }
183+
184+ // check if leaf (throw if not)
185+ if (leaf is ! LeafRoute ) {
186+ throw ArgumentError .value (
187+ path,
188+ 'path' ,
189+ 'Resolved route is not a leaf!' ,
190+ );
191+ }
192+
193+ leaf as LeafRoute <T , Config >;
194+
195+ final future = _navigate (uri, leaf, skipPreview, callback);
196+
197+ _pendingNavigations[uri] = (future, leaf);
198+ await future;
199+ } catch (e, s) {
200+ log ('Navigation to $path failed with error' , e, s);
201+ rethrow ;
202+ } finally {
203+ _pendingNavigations.remove (uri);
204+ }
205+ }
206+
207+ Future <bool > _navigate (
208+ Uri uri,
209+ LeafRoute <T , Config > leaf,
210+ bool skipPreview,
211+ void Function (T , bool ) callback,
212+ ) async {
161213 var context = RouteContext .fromUri (uri);
214+ final cleanPath = uri.path;
162215
163- log ('Navigating to $path with context: $context ' );
216+ log ('Navigating to $cleanPath with context: $context ' );
164217
165- if (! skipPreview) callback (leaf.view.preview (context));
218+ if (! skipPreview) callback (leaf.view.preview (context), true );
166219
167220 // activate required modules
168221 final dependencies = getDependencies (cleanPath);
169222
170223 for (Module <T , Config > module in dependencies) {
171- module.activate ();
224+ await module.activate ();
172225 log ('Activated module: ${module .runtimeType }' );
173226 }
174227
175228 // run middlewares (if any)
176229 try {
177- for (final middleware in leaf.middleware) {
230+ for (var i = 0 ; i < leaf.middleware.length; i++ ) {
231+ final middleware = leaf.middleware[i];
232+ log (
233+ 'Executing middleware ${i + 1 }/${leaf .middleware .length }: ${middleware .runtimeType }' ,
234+ );
178235 context = await middleware (context);
179- log ('Middleware processed context: $context ' );
180236 }
237+ log (
238+ 'All ${leaf .middleware .length } middlewares executed successfully for $cleanPath ' ,
239+ );
181240 } catch (e, s) {
182- log ('A middleware threw an exception during navigation to $path ' , e, s);
183- rethrow ;
241+ log (
242+ 'A middleware threw an exception during navigation to $cleanPath ' ,
243+ e,
244+ s,
245+ );
246+ return false ;
184247 }
185248
186249 _context = context;
187250
188- callback (await leaf.view.content (context));
251+ callback (await leaf.view.content (context), false );
189252
190- log ('Activated route at $path ' );
253+ log ('Activated route at $cleanPath ' );
191254
192255 // notify listeners
193256 for (final listener in _listeners) {
194257 listener (leaf);
195258 }
259+
260+ return true ;
196261 }
197262}
0 commit comments