router.dart 7.4 KB

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