|
|
|
|
|
|
using System; |
|
|
|
using System.Collections; |
|
|
|
using Unity.UIWidgets.external.simplejson; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using Unity.UIWidgets.gestures; |
|
|
|
using Unity.UIWidgets.rendering; |
|
|
|
|
|
|
routeEntry = historyEntry; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
return routeEntry?.isPresent == true; |
|
|
|
} |
|
|
|
return routeEntry?.isPresent == true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public class RouteSettings { |
|
|
|
public RouteSettings(string name = null, bool isInitialRoute = false, object arguments = null) { |
|
|
|
public RouteSettings( |
|
|
|
string name = null, |
|
|
|
object arguments = null) { |
|
|
|
this.isInitialRoute = isInitialRoute; |
|
|
|
RouteSettings copyWith(string name = null, bool? isInitialRoute = null, object arguments = null) { |
|
|
|
RouteSettings copyWith( |
|
|
|
string name = null, |
|
|
|
|
|
|
|
object arguments = null) { |
|
|
|
isInitialRoute ?? this.isInitialRoute, |
|
|
|
public readonly bool isInitialRoute; |
|
|
|
|
|
|
|
public readonly string name; |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public abstract class Page<T> : RouteSettings { |
|
|
|
public abstract class Page : RouteSettings { |
|
|
|
public Page( |
|
|
|
Key key = null, |
|
|
|
string name = null, |
|
|
|
|
|
|
|
|
|
|
public LocalKey key; |
|
|
|
|
|
|
|
public bool canUpdate(Page<object> other) { |
|
|
|
public bool canUpdate(Page other) { |
|
|
|
public abstract Route<T> createRoute(BuildContext context); |
|
|
|
//public abstract Route createRoute(BuildContext context);
|
|
|
|
|
|
|
|
public override string ToString() { |
|
|
|
return $"{GetType()}(\"{name}\",{key},{arguments})"; |
|
|
|
|
|
|
/*public class Page<T> : Page { |
|
|
|
readonly Page _page; |
|
|
|
|
|
|
|
public abstract class Page<T> : Page { |
|
|
|
public Page( |
|
|
|
Key key = null, |
|
|
|
string name = null, |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public bool canUpdate(Page<object> other) { |
|
|
|
return other.GetType() == GetType() && |
|
|
|
other.key == key; |
|
|
|
} |
|
|
|
|
|
|
|
public Route<T> createRoute(BuildContext context) { |
|
|
|
//return new Route<T>(context);
|
|
|
|
} |
|
|
|
}*/ |
|
|
|
|
|
|
|
public abstract Route<T> createRoute(BuildContext context); |
|
|
|
} |
|
|
|
|
|
|
|
public class CustomBuilderPage<T> : Page<T> { |
|
|
|
public CustomBuilderPage( |
|
|
|
|
|
|
object arguments = null |
|
|
|
) : base(key: key, name: name, arguments: arguments) { |
|
|
|
D.assert(key != null); |
|
|
|
D.assert(routeBuilder != null); |
|
|
|
this.routeBuilder = routeBuilder; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public IEnumerable<RouteTransitionRecord> _transition( |
|
|
|
List<RouteTransitionRecord> newPageRouteHistory, |
|
|
|
Dictionary<RouteTransitionRecord, RouteTransitionRecord> locationToExitingPageRoute, |
|
|
|
Dictionary<RouteTransitionRecord, List<RouteTransitionRecord>> pageRouteToPagelessRoutes |
|
|
|
List<RouteTransitionRecord> newPageRouteHistory = null, |
|
|
|
Dictionary<RouteTransitionRecord, RouteTransitionRecord> locationToExitingPageRoute = null, |
|
|
|
Dictionary<RouteTransitionRecord, List<RouteTransitionRecord>> pageRouteToPagelessRoutes = null |
|
|
|
) { |
|
|
|
IEnumerable<RouteTransitionRecord> results = resolve( |
|
|
|
newPageRouteHistory: newPageRouteHistory, |
|
|
|
|
|
|
foreach(RouteTransitionRecord item in locationToExitingPageRoute.Values) { |
|
|
|
exitingPageRoutes.Add(item); |
|
|
|
} |
|
|
|
|
|
|
|
// Firstly, verifies all exiting routes have been marked.
|
|
|
|
foreach (RouteTransitionRecord exitingPageRoute in exitingPageRoutes) { |
|
|
|
D.assert(!exitingPageRoute._debugWaitingForExitDecision); |
|
|
|
if (pageRouteToPagelessRoutes.ContainsKey(exitingPageRoute)) { |
|
|
|
|
|
|
List<RouteTransitionRecord> newPageRouteHistory = null, |
|
|
|
Dictionary<RouteTransitionRecord, RouteTransitionRecord> locationToExitingPageRoute = null, |
|
|
|
Dictionary<RouteTransitionRecord, List<RouteTransitionRecord>> pageRouteToPagelessRoutes = null) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void handleExitingRoute(RouteTransitionRecord location, bool isLast) { |
|
|
|
RouteTransitionRecord exitingPageRoute = locationToExitingPageRoute[location]; |
|
|
|
if (exitingPageRoute == null) |
|
|
|
|
|
|
|
|
|
|
public static readonly string defaultRouteName = "/"; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Future<T> pushNamed<T extends object>(
|
|
|
|
|
|
|
|
public static Future<T> pushNamed<T>(BuildContext context, string routeName, object arguments = null) { |
|
|
|
return of(context).pushNamed<T>(routeName, arguments: arguments); |
|
|
|
|
|
|
TO result , object arguments = null) { |
|
|
|
TO result = default , object arguments = null) { |
|
|
|
TO result , |
|
|
|
TO result = default, |
|
|
|
object arguments = null) { |
|
|
|
return of(context).popAndPushNamed<T,TO>(routeName, result: result, arguments: arguments); |
|
|
|
} |
|
|
|
|
|
|
return of(context).push<T>(route); |
|
|
|
} |
|
|
|
|
|
|
|
public static Future<T> pushReplacement<T,TO>(BuildContext context, Route<T> newRoute, TO result ) { |
|
|
|
public static Future<T> pushReplacement<T,TO>(BuildContext context, Route<T> newRoute, TO result = default ) { |
|
|
|
return of(context).pushReplacement<T,TO>(newRoute, result); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public static void replace<T>(BuildContext context, Route oldRoute, Route<T> newRoute ) { |
|
|
|
public static void replace<T>(BuildContext context, Route oldRoute = null, Route<T> newRoute = null) { |
|
|
|
public static void replaceRouteBelow<T>(BuildContext context, Route anchorRoute, Route<T> newRoute ) { |
|
|
|
public static void replaceRouteBelow<T>(BuildContext context, Route anchorRoute = null, Route<T> newRoute = null ) { |
|
|
|
of(context).replaceRouteBelow<T>(anchorRoute: anchorRoute, newRoute: newRoute); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public static void removeRouteBelow(BuildContext context, Route anchorRoute) { |
|
|
|
of(context).removeRouteBelow(anchorRoute); |
|
|
|
} |
|
|
|
/*public static void replace(BuildContext context, Route oldRoute = null, Route newRoute = null) { |
|
|
|
D.assert(oldRoute != null); |
|
|
|
D.assert(newRoute != null); |
|
|
|
of(context).replace(oldRoute: oldRoute, newRoute: newRoute); |
|
|
|
} |
|
|
|
|
|
|
|
public static void replaceRouteBelow(BuildContext context, Route anchorRoute = null, Route newRoute = null) { |
|
|
|
D.assert(anchorRoute != null); |
|
|
|
D.assert(newRoute != null); |
|
|
|
of(context).replaceRouteBelow(anchorRoute: anchorRoute, newRoute: newRoute); |
|
|
|
} |
|
|
|
|
|
|
|
public static bool canPop(BuildContext context) { |
|
|
|
NavigatorState navigator = of(context, nullOk: true); |
|
|
|
return navigator != null && navigator.canPop(); |
|
|
|
} |
|
|
|
|
|
|
|
public static Future<bool> maybePop(BuildContext context, object result = null) { |
|
|
|
return of(context).maybePop(result); |
|
|
|
} |
|
|
|
|
|
|
|
public static bool pop(BuildContext context, object result = null) { |
|
|
|
return of(context).pop(result); |
|
|
|
} |
|
|
|
|
|
|
|
public static void popUntil(BuildContext context, RoutePredicate predicate) { |
|
|
|
of(context).popUntil(predicate); |
|
|
|
} |
|
|
|
|
|
|
|
public static void removeRoute(BuildContext context, Route route) { |
|
|
|
of(context).removeRoute(route); |
|
|
|
} |
|
|
|
|
|
|
|
static void removeRouteBelow(BuildContext context, Route anchorRoute) { |
|
|
|
of(context).removeRouteBelow(anchorRoute); |
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
|
|
public static NavigatorState of( |
|
|
|
BuildContext context, |
|
|
|
bool rootNavigator = false, |
|
|
|
|
|
|
_RouteLifecycle initialState |
|
|
|
|
|
|
|
) { |
|
|
|
D.assert(route != null); |
|
|
|
D.assert(initialState != null); |
|
|
|
D.assert( |
|
|
|
initialState == _RouteLifecycle.staging || |
|
|
|
initialState == _RouteLifecycle.add || |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public Route route; |
|
|
|
|
|
|
|
public _RouteLifecycle currentState; |
|
|
|
public Route lastAnnouncedPreviousRoute; // last argument to Route.didChangePrevious
|
|
|
|
public Route lastAnnouncedPoppedNextRoute; // last argument to Route.didPopNext
|
|
|
|
|
|
|
get { return route.settings is Page<object>; } |
|
|
|
get { return route.settings is Page; } |
|
|
|
public bool canUpdateFrom(Page<object> page) { |
|
|
|
public bool canUpdateFrom(Page page) { |
|
|
|
Page<object> routePage = route.settings as Page<object>; |
|
|
|
Page routePage = route.settings as Page; |
|
|
|
return page.canUpdate(routePage); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
lastAnnouncedPoppedNextRoute = poppedRoute; |
|
|
|
} |
|
|
|
|
|
|
|
public void handlePop(NavigatorState navigator, Route previousPresent) { |
|
|
|
public void handlePop(NavigatorState navigator , Route previousPresent = null) { |
|
|
|
D.assert(navigator != null); |
|
|
|
D.assert(navigator._debugLocked); |
|
|
|
D.assert(route._navigator == navigator); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public void handleRemoval(NavigatorState navigator, Route previousPresent) { |
|
|
|
public void handleRemoval(NavigatorState navigator, Route previousPresent = null) { |
|
|
|
D.assert(navigator != null); |
|
|
|
D.assert(navigator._debugLocked); |
|
|
|
D.assert(route._navigator == navigator); |
|
|
|
|
|
|
|
|
|
|
public bool doingPop = false; |
|
|
|
|
|
|
|
public void didAdd(NavigatorState navigator, bool isNewFirst, Route previous, Route previousPresent) { |
|
|
|
public void didAdd( |
|
|
|
NavigatorState navigator = null, |
|
|
|
bool isNewFirst = false, |
|
|
|
Route previous = null, |
|
|
|
Route previousPresent = null) { |
|
|
|
|
|
|
|
route.didAdd(); |
|
|
|
currentState = _RouteLifecycle.idle; |
|
|
|
if (isNewFirst) { |
|
|
|
|
|
|
|
|
|
|
bool _reportRemovalToObserver = true; |
|
|
|
|
|
|
|
// Route is removed without being completed.
|
|
|
|
|
|
|
|
public void remove(bool isReplaced = false) { |
|
|
|
D.assert( |
|
|
|
!hasPage || _debugWaitingForExitDecision, () => |
|
|
|
|
|
|
currentState = _RouteLifecycle.remove; |
|
|
|
} |
|
|
|
|
|
|
|
// Route completes with `result` and is removed.
|
|
|
|
|
|
|
|
public void complete<T>(T result, bool isReplaced = false) { |
|
|
|
D.assert( |
|
|
|
!hasPage || _debugWaitingForExitDecision, () => |
|
|
|
|
|
|
public void dispose() { |
|
|
|
D.assert(currentState.GetHashCode() < _RouteLifecycle.disposed.GetHashCode()); |
|
|
|
((Route)route).dispose(); |
|
|
|
//route._navigator = null;
|
|
|
|
currentState = _RouteLifecycle.disposed; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
"This route cannot be marked for pop. Either a decision has already been " + |
|
|
|
"made or it does not require an explicit decision on how to transition out." |
|
|
|
); |
|
|
|
pop<object>(result); |
|
|
|
pop(result); |
|
|
|
_debugWaitingForExitDecision = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
"been made or it does not require an explicit decision on how to transition " + |
|
|
|
"out." |
|
|
|
); |
|
|
|
complete<object>(result); |
|
|
|
complete(result); |
|
|
|
_debugWaitingForExitDecision = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string initialRoute = widget.initialRoute; |
|
|
|
if (widget.pages.isNotEmpty()) { |
|
|
|
foreach (Page<object> page in widget.pages) { |
|
|
|
foreach (Page<object> page in widget.pages) { |
|
|
|
_history.Add( |
|
|
|
new _RouteEntry( |
|
|
|
page.createRoute(context), |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (initialRoute != null) { |
|
|
|
foreach (Route route in |
|
|
|
(widget.onGenerateInitialRoutes(this, widget.initialRoute |
|
|
|
?? Navigator.defaultRouteName))) { |
|
|
|
_history.Add( |
|
|
|
new _RouteEntry( |
|
|
|
route, |
|
|
|
initialState: _RouteLifecycle.add |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
_history.AddRange( |
|
|
|
widget.onGenerateInitialRoutes( |
|
|
|
this, |
|
|
|
widget.initialRoute ?? Navigator.defaultRouteName |
|
|
|
).Select((Route route) => |
|
|
|
new _RouteEntry( |
|
|
|
route, |
|
|
|
initialState: _RouteLifecycle.add |
|
|
|
) |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
D.assert(!_debugLocked); |
|
|
|
D.assert(() => { |
|
|
|
_debugLocked = true; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public new void didUpdateWidget(Navigator oldWidget) { |
|
|
|
// oldWidget = (Navigator) oldWidget;
|
|
|
|
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|
|
|
oldWidget = (Navigator) oldWidget; |
|
|
|
base.didUpdateWidget(oldWidget); |
|
|
|
D.assert( |
|
|
|
widget.pages.isEmpty() || widget.onPopPage != null, () => |
|
|
|
|
|
|
void _debugCheckDuplicatedPageKeys() { |
|
|
|
D.assert(() => { |
|
|
|
List<Key> keyReservation = new List<Key>(); |
|
|
|
foreach (Page<object> page in widget.pages) { |
|
|
|
foreach (Page page in widget.pages) { |
|
|
|
if (page.key != null) { |
|
|
|
D.assert(!keyReservation.Contains(page.key)); |
|
|
|
keyReservation.Add(page.key); |
|
|
|
|
|
|
}); |
|
|
|
foreach (NavigatorObserver observer in widget.observers) |
|
|
|
observer._navigator = null; |
|
|
|
focusScopeNode.dispose();/// focus manager dispose
|
|
|
|
focusScopeNode.dispose(); |
|
|
|
// don"t unlock, so that the object becomes unusable
|
|
|
|
D.assert(_debugLocked); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (newPagesBottom > newPagesTop) |
|
|
|
break; |
|
|
|
Page<object> newPage = widget.pages[newPagesBottom]; |
|
|
|
Page newPage = widget.pages[newPagesBottom]; |
|
|
|
if (!oldEntry.canUpdateFrom(newPage)) |
|
|
|
break; |
|
|
|
previousOldPageRouteEntry = oldEntry; |
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
Page<object> newPage = widget.pages[newPagesTop]; |
|
|
|
Page newPage = widget.pages[newPagesTop]; |
|
|
|
if (!oldEntry.canUpdateFrom(newPage)) |
|
|
|
break; |
|
|
|
pagelessRoutesToSkip = 0; |
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
D.assert(oldEntry.hasPage); |
|
|
|
Page<object> page = oldEntry.route.settings as Page<object>; |
|
|
|
Page page = oldEntry.route.settings as Page; |
|
|
|
if (page.key == null) |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
Page<object> potentialPageToRemove = potentialEntryToRemove.route.settings as Page<object>; |
|
|
|
Page potentialPageToRemove = potentialEntryToRemove.route.settings as Page; |
|
|
|
|
|
|
|
if ( |
|
|
|
potentialPageToRemove.key == null || |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
previousOldPageRouteEntry = oldEntry; |
|
|
|
Page<object> newPage = widget.pages[newPagesBottom]; |
|
|
|
Page newPage = widget.pages[newPagesBottom]; |
|
|
|
D.assert(oldEntry.canUpdateFrom(newPage)); |
|
|
|
oldEntry.route._updateSettings(newPage); |
|
|
|
newHistory.Add(oldEntry); |
|
|
|
|
|
|
} |
|
|
|
results = widget.transitionDelegate._transition( |
|
|
|
newPageRouteHistory:newHistoryRecord, |
|
|
|
// List<RouteTransitionRecord> : newHistory = List<_RouteEntry>
|
|
|
|
locationToExitingPageRoute: locationToExitingPageRoute, |
|
|
|
pageRouteToPagelessRoutes: pageRouteToPagelessRoutesRecord |
|
|
|
).Cast<_RouteEntry>(); |
|
|
|
|
|
|
// Adds the leading pageless routes if there is any.
|
|
|
|
if (pageRouteToPagelessRoutes.ContainsKey(null)) { |
|
|
|
foreach (var pageRoute in pageRouteToPagelessRoutes[null]) { |
|
|
|
_history.Add(pageRoute); |
|
|
|
} |
|
|
|
//_history.addAll(pageRouteToPagelessRoutes[null]);
|
|
|
|
_history.AddRange(pageRouteToPagelessRoutes[null]); |
|
|
|
//_history.addAll(pageRouteToPagelessRoutes[result]);
|
|
|
|
foreach (var pageRoute in pageRouteToPagelessRoutes[result]) { |
|
|
|
_history.Add(pageRoute); |
|
|
|
} |
|
|
|
_history.AddRange(pageRouteToPagelessRoutes[result]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
_flushRouteAnnouncement(); |
|
|
|
|
|
|
|
|
|
|
|
/* _RouteEntry lastEntry = null;//_history.lastWhere(_RouteEntry.isPresentPredicate, orElse: () => null);
|
|
|
|
_RouteEntry lastEntry = null;//_history.lastWhere(_RouteEntry.isPresentPredicate, orElse: () => null);
|
|
|
|
foreach (var historyEntry in _history) { |
|
|
|
if (_RouteEntry.isPresentPredicate(historyEntry)) { |
|
|
|
lastEntry = historyEntry; |
|
|
|
|
|
|
if (routeName != _lastAnnouncedRouteName) { |
|
|
|
//RouteNotificationMessages.maybeNotifyRouteChange(routeName, _lastAnnouncedRouteName);
|
|
|
|
//RouteNotificationMessages.maybeNotifyRouteChange(routeName, _lastAnnouncedRouteName);
|
|
|
|
}*/ |
|
|
|
} |
|
|
|
|
|
|
|
// Lastly, removes the overlay entries of all marked entries and disposes
|
|
|
|
// them.
|
|
|
|
|
|
|
|
|
|
|
public Future<T> pushReplacementNamed<T, TO>( |
|
|
|
string routeName, |
|
|
|
TO result, |
|
|
|
TO result = default, |
|
|
|
object arguments = null |
|
|
|
) { |
|
|
|
return pushReplacement<T, TO>(_routeNamed<T>(routeName, arguments: arguments), result: result); |
|
|
|
|
|
|
string routeName, |
|
|
|
TO result, |
|
|
|
TO result = default, |
|
|
|
object arguments = null |
|
|
|
) { |
|
|
|
pop<TO>(result); |
|
|
|
|
|
|
_afterNavigation(route); |
|
|
|
return route.popped.to<T>(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*if (!kReleaseMode) { |
|
|
|
if (!foundation_.kReleaseMode) { |
|
|
|
if (route is TransitionRoute<T>) { |
|
|
|
TransitionRoute<T> transitionRoute = (TransitionRoute<T>)route; |
|
|
|
description = transitionRoute.debugLabel; |
|
|
|
if (route is TransitionRoute ){ |
|
|
|
//TransitionRoute transitionRoute = route;
|
|
|
|
description = ((TransitionRoute)route).debugLabel; |
|
|
|
description = "$route"; |
|
|
|
description = $"{route}"; |
|
|
|
} |
|
|
|
|
|
|
|
routeJsonable["description"] = description; |
|
|
|
|
|
|
"name": settings.name |
|
|
|
{"name", settings.name} |
|
|
|
if (settings.arguments != null) { |
|
|
|
/*if (settings.arguments != null) { |
|
|
|
toEncodable: (object object) => "$object" |
|
|
|
); |
|
|
|
} |
|
|
|
toEncodable: (object _object) => $"{_object}" |
|
|
|
); |
|
|
|
}*/ |
|
|
|
|
|
|
|
developer.postEvent("Flutter.Navigation", < string, object >{ |
|
|
|
"route": routeJsonable |
|
|
|
// todo
|
|
|
|
developer.developer_.postEvent("Flutter.Navigation", new Hashtable{ |
|
|
|
{"route", routeJsonable} |
|
|
|
}*/ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Future<T> pushReplacement<T, TO>(Route<T> newRoute, TO result) { |
|
|
|
D.assert(!_debugLocked); |
|
|
|
|
|
|
_debugLocked = true; |
|
|
|
return true; |
|
|
|
}); |
|
|
|
//D.assert(_history.where(_RouteEntry.isRoutePredicate(route)).length == 1);
|
|
|
|
// D.assert(_history.Where(_RouteEntry.isRoutePredicate(route)).Count() == 1);
|
|
|
|
List<_RouteEntry> routeEntries = new List<_RouteEntry>(); |
|
|
|
foreach (var historyEntry in _history) { |
|
|
|
if (_RouteEntry.isRoutePredicate(route)(historyEntry)) { |
|
|
|