router.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * fluro
  3. * Created by Yakka
  4. * https://theyakka.com
  5. *
  6. * Copyright (c) 2019 Yakka, LLC. All rights reserved.
  7. * See LICENSE for distribution and usage details.
  8. */
  9. import 'dart:async';
  10. import 'package:fluro/fluro.dart';
  11. import 'package:fluro/src/common.dart';
  12. import 'package:flutter/cupertino.dart' hide Router;
  13. import 'package:flutter/material.dart' hide Router;
  14. import 'package:flutter/material.dart' as material;
  15. /// Router has been renamed to [FluroRouter] to avoid namespace collisions with
  16. /// Flutter's new [material.Router] class from the Pages / Navigator 2.0 API.
  17. ///
  18. /// [FluroRouter] is identical to [Router].
  19. ///
  20. /// This will no longer be updated after version `1.6.4`.
  21. @Deprecated('Use FluroRouter instead')
  22. class Router {
  23. static final appRouter = Router();
  24. /// The tree structure that stores the defined routes
  25. final RouteTree _routeTree = RouteTree();
  26. /// Generic handler for when a route has not been defined
  27. Handler notFoundHandler;
  28. /// Creates a [PageRoute] definition for the passed [RouteHandler]. You can optionally provide a default transition type.
  29. void define(String routePath,
  30. {@required Handler handler, TransitionType transitionType}) {
  31. _routeTree.addRoute(
  32. AppRoute(routePath, handler, transitionType: transitionType),
  33. );
  34. }
  35. /// Finds a defined [AppRoute] for the path value. If no [AppRoute] definition was found
  36. /// then function will return null.
  37. AppRouteMatch match(String path) {
  38. return _routeTree.matchRoute(path);
  39. }
  40. void pop<T>(BuildContext context, [T result]) =>
  41. Navigator.of(context).pop(result);
  42. ///
  43. Future navigateTo(BuildContext context, String path,
  44. {bool replace = false,
  45. bool clearStack = false,
  46. TransitionType transition,
  47. Duration transitionDuration = const Duration(milliseconds: 250),
  48. RouteTransitionsBuilder transitionBuilder}) {
  49. RouteMatch routeMatch = matchRoute(context, path,
  50. transitionType: transition,
  51. transitionsBuilder: transitionBuilder,
  52. transitionDuration: transitionDuration);
  53. Route<dynamic> route = routeMatch.route;
  54. Completer completer = Completer();
  55. Future future = completer.future;
  56. if (routeMatch.matchType == RouteMatchType.nonVisual) {
  57. completer.complete("Non visual route type.");
  58. } else {
  59. if (route == null && notFoundHandler != null) {
  60. route = _notFoundRoute(context, path);
  61. }
  62. if (route != null) {
  63. if (clearStack) {
  64. future =
  65. Navigator.of(context).pushAndRemoveUntil(route, (check) => false);
  66. } else {
  67. future = replace
  68. ? Navigator.of(context).pushReplacement(route)
  69. : Navigator.of(context).push(route);
  70. }
  71. completer.complete();
  72. } else {
  73. String error = "No registered route was found to handle '$path'.";
  74. print(error);
  75. completer.completeError(RouteNotFoundException(error, path));
  76. }
  77. }
  78. return future;
  79. }
  80. ///
  81. Route<Null> _notFoundRoute(BuildContext context, String path) {
  82. RouteCreator<Null> creator =
  83. (RouteSettings routeSettings, Map<String, List<String>> parameters) {
  84. return MaterialPageRoute<Null>(
  85. settings: routeSettings,
  86. builder: (BuildContext context) {
  87. return notFoundHandler.handlerFunc(context, parameters);
  88. });
  89. };
  90. return creator(RouteSettings(name: path), null);
  91. }
  92. ///
  93. RouteMatch matchRoute(BuildContext buildContext, String path,
  94. {RouteSettings routeSettings,
  95. TransitionType transitionType,
  96. Duration transitionDuration = const Duration(milliseconds: 250),
  97. RouteTransitionsBuilder transitionsBuilder}) {
  98. RouteSettings settingsToUse = routeSettings;
  99. if (routeSettings == null) {
  100. settingsToUse = RouteSettings(name: path);
  101. }
  102. AppRouteMatch match = _routeTree.matchRoute(path);
  103. AppRoute route = match?.route;
  104. Handler handler = (route != null ? route.handler : notFoundHandler);
  105. var transition = transitionType;
  106. if (transitionType == null) {
  107. transition = route != null ? route.transitionType : TransitionType.native;
  108. }
  109. if (route == null && notFoundHandler == null) {
  110. return RouteMatch(
  111. matchType: RouteMatchType.noMatch,
  112. errorMessage: "No matching route was found");
  113. }
  114. Map<String, List<String>> parameters =
  115. match?.parameters ?? <String, List<String>>{};
  116. if (handler.type == HandlerType.function) {
  117. handler.handlerFunc(buildContext, parameters);
  118. return RouteMatch(matchType: RouteMatchType.nonVisual);
  119. }
  120. RouteCreator creator =
  121. (RouteSettings routeSettings, Map<String, List<String>> parameters) {
  122. bool isNativeTransition = (transition == TransitionType.native ||
  123. transition == TransitionType.nativeModal);
  124. if (isNativeTransition) {
  125. if (Theme.of(buildContext).platform == TargetPlatform.iOS) {
  126. return CupertinoPageRoute<dynamic>(
  127. settings: routeSettings,
  128. fullscreenDialog: transition == TransitionType.nativeModal,
  129. builder: (BuildContext context) {
  130. return handler.handlerFunc(context, parameters);
  131. });
  132. } else {
  133. return MaterialPageRoute<dynamic>(
  134. settings: routeSettings,
  135. fullscreenDialog: transition == TransitionType.nativeModal,
  136. builder: (BuildContext context) {
  137. return handler.handlerFunc(context, parameters);
  138. });
  139. }
  140. } else if (transition == TransitionType.material ||
  141. transition == TransitionType.materialFullScreenDialog) {
  142. return MaterialPageRoute<dynamic>(
  143. settings: routeSettings,
  144. fullscreenDialog:
  145. transition == TransitionType.materialFullScreenDialog,
  146. builder: (BuildContext context) {
  147. return handler.handlerFunc(context, parameters);
  148. });
  149. } else if (transition == TransitionType.cupertino ||
  150. transition == TransitionType.cupertinoFullScreenDialog) {
  151. return CupertinoPageRoute<dynamic>(
  152. settings: routeSettings,
  153. fullscreenDialog:
  154. transition == TransitionType.cupertinoFullScreenDialog,
  155. builder: (BuildContext context) {
  156. return handler.handlerFunc(context, parameters);
  157. });
  158. } else {
  159. var routeTransitionsBuilder;
  160. if (transition == TransitionType.custom) {
  161. routeTransitionsBuilder = transitionsBuilder;
  162. } else {
  163. routeTransitionsBuilder = _standardTransitionsBuilder(transition);
  164. }
  165. return PageRouteBuilder<dynamic>(
  166. settings: routeSettings,
  167. pageBuilder: (BuildContext context, Animation<double> animation,
  168. Animation<double> secondaryAnimation) {
  169. return handler.handlerFunc(context, parameters);
  170. },
  171. transitionDuration: transitionDuration,
  172. transitionsBuilder: routeTransitionsBuilder,
  173. );
  174. }
  175. };
  176. return RouteMatch(
  177. matchType: RouteMatchType.visual,
  178. route: creator(settingsToUse, parameters),
  179. );
  180. }
  181. RouteTransitionsBuilder _standardTransitionsBuilder(
  182. TransitionType transitionType) {
  183. return (BuildContext context, Animation<double> animation,
  184. Animation<double> secondaryAnimation, Widget child) {
  185. if (transitionType == TransitionType.fadeIn) {
  186. return FadeTransition(opacity: animation, child: child);
  187. } else {
  188. const Offset topLeft = const Offset(0.0, 0.0);
  189. const Offset topRight = const Offset(1.0, 0.0);
  190. const Offset bottomLeft = const Offset(0.0, 1.0);
  191. Offset startOffset = bottomLeft;
  192. Offset endOffset = topLeft;
  193. if (transitionType == TransitionType.inFromLeft) {
  194. startOffset = const Offset(-1.0, 0.0);
  195. endOffset = topLeft;
  196. } else if (transitionType == TransitionType.inFromRight) {
  197. startOffset = topRight;
  198. endOffset = topLeft;
  199. }
  200. return SlideTransition(
  201. position: Tween<Offset>(
  202. begin: startOffset,
  203. end: endOffset,
  204. ).animate(animation),
  205. child: child,
  206. );
  207. }
  208. };
  209. }
  210. /// Route generation method. This function can be used as a way to create routes on-the-fly
  211. /// if any defined handler is found. It can also be used with the [MaterialApp.onGenerateRoute]
  212. /// property as callback to create routes that can be used with the [Navigator] class.
  213. Route<dynamic> generator(RouteSettings routeSettings) {
  214. RouteMatch match =
  215. matchRoute(null, routeSettings.name, routeSettings: routeSettings);
  216. return match.route;
  217. }
  218. /// Prints the route tree so you can analyze it.
  219. void printTree() {
  220. _routeTree.printTree();
  221. }
  222. }