generate_router.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import 'package:appflowy/mobile/presentation/presentation.dart';
  2. import 'package:appflowy/startup/startup.dart';
  3. import 'package:appflowy/startup/tasks/app_widget.dart';
  4. import 'package:appflowy/user/application/auth/auth_service.dart';
  5. import 'package:appflowy/user/presentation/presentation.dart';
  6. import 'package:appflowy/util/platform_extension.dart';
  7. import 'package:appflowy/workspace/presentation/home/desktop_home_screen.dart';
  8. import 'package:flowy_infra/time/duration.dart';
  9. import 'package:flutter/material.dart';
  10. import 'package:go_router/go_router.dart';
  11. GoRouter generateRouter(Widget child) {
  12. return GoRouter(
  13. navigatorKey: AppGlobals.rootNavKey,
  14. initialLocation: '/',
  15. routes: [
  16. // Root route is SplashScreen.
  17. // It needs LaunchConfiguration as a parameter, so we get it from ApplicationWidget's child.
  18. _rootRoute(child),
  19. // Routes in both desktop and mobile
  20. _signInScreenRoute(),
  21. _skipLogInScreenRoute(),
  22. _encryptSecretScreenRoute(),
  23. _workspaceErrorScreenRoute(),
  24. // Desktop only
  25. if (!PlatformExtension.isMobile) _desktopHomeScreenRoute(),
  26. // Mobile only
  27. if (PlatformExtension.isMobile) _mobileHomeScreenWithNavigationBarRoute(),
  28. // Unused routes for now, it may need to be used in the future.
  29. // TODO(yijing): extract route method like other routes when it comes to be used.
  30. // Desktop and Mobile
  31. GoRoute(
  32. path: WorkspaceStartScreen.routeName,
  33. pageBuilder: (context, state) {
  34. final args = state.extra as Map<String, dynamic>;
  35. return CustomTransitionPage(
  36. child: WorkspaceStartScreen(
  37. userProfile: args[WorkspaceStartScreen.argUserProfile],
  38. ),
  39. transitionsBuilder: _buildFadeTransition,
  40. transitionDuration: _slowDuration,
  41. );
  42. },
  43. ),
  44. GoRoute(
  45. path: SignUpScreen.routeName,
  46. pageBuilder: (context, state) {
  47. return CustomTransitionPage(
  48. child: SignUpScreen(
  49. router: getIt<AuthRouter>(),
  50. ),
  51. transitionsBuilder: _buildFadeTransition,
  52. transitionDuration: _slowDuration,
  53. );
  54. },
  55. ),
  56. ],
  57. );
  58. }
  59. /// We use StatefulShellRoute to create a StatefulNavigationShell(ScaffoldWithNavBar) to access to multiple pages, and each page retains its own state.
  60. StatefulShellRoute _mobileHomeScreenWithNavigationBarRoute() {
  61. return StatefulShellRoute.indexedStack(
  62. builder: (
  63. BuildContext context,
  64. GoRouterState state,
  65. StatefulNavigationShell navigationShell,
  66. ) {
  67. // Return the widget that implements the custom shell (in this case
  68. // using a BottomNavigationBar). The StatefulNavigationShell is passed
  69. // to be able access the state of the shell and to navigate to other
  70. // branches in a stateful way.
  71. return MobileBottomNavigationBar(navigationShell: navigationShell);
  72. },
  73. branches: <StatefulShellBranch>[
  74. StatefulShellBranch(
  75. routes: <RouteBase>[
  76. GoRoute(
  77. // The screen to display as the root in the first tab of the
  78. // bottom navigation bar.
  79. path: MobileHomeScreen.routeName,
  80. builder: (BuildContext context, GoRouterState state) {
  81. return const MobileHomeScreen();
  82. },
  83. ),
  84. ],
  85. ),
  86. // TODO(yijing): implement other tabs later
  87. // The following code comes from the example of StatefulShellRoute.indexedStack. I left there just for placeholder purpose. They will be updated in the future.
  88. // The route branch for the second tab of the bottom navigation bar.
  89. StatefulShellBranch(
  90. // It's not necessary to provide a navigatorKey if it isn't also
  91. // needed elsewhere. If not provided, a default key will be used.
  92. routes: <RouteBase>[
  93. GoRoute(
  94. // The screen to display as the root in the second tab of the
  95. // bottom navigation bar.
  96. path: '/b',
  97. builder: (BuildContext context, GoRouterState state) =>
  98. const RootPlaceholderScreen(
  99. label: 'Favorite',
  100. detailsPath: '/b/details/1',
  101. secondDetailsPath: '/b/details/2',
  102. ),
  103. routes: <RouteBase>[
  104. GoRoute(
  105. path: 'details/:param',
  106. builder: (BuildContext context, GoRouterState state) =>
  107. DetailsPlaceholderScreen(
  108. label: 'Favorite details',
  109. param: state.pathParameters['param'],
  110. ),
  111. ),
  112. ],
  113. ),
  114. ],
  115. ),
  116. // The route branch for the third tab of the bottom navigation bar.
  117. StatefulShellBranch(
  118. routes: <RouteBase>[
  119. GoRoute(
  120. // The screen to display as the root in the third tab of the
  121. // bottom navigation bar.
  122. path: '/c',
  123. builder: (BuildContext context, GoRouterState state) =>
  124. const RootPlaceholderScreen(
  125. label: 'Add Document',
  126. detailsPath: '/c/details',
  127. ),
  128. routes: <RouteBase>[
  129. GoRoute(
  130. path: 'details',
  131. builder: (BuildContext context, GoRouterState state) =>
  132. DetailsPlaceholderScreen(
  133. label: 'Add Document details',
  134. extra: state.extra,
  135. ),
  136. ),
  137. ],
  138. ),
  139. ],
  140. ),
  141. StatefulShellBranch(
  142. routes: <RouteBase>[
  143. GoRoute(
  144. path: '/d',
  145. builder: (BuildContext context, GoRouterState state) =>
  146. const RootPlaceholderScreen(
  147. label: 'Search',
  148. detailsPath: '/d/details',
  149. ),
  150. routes: <RouteBase>[
  151. GoRoute(
  152. path: 'details',
  153. builder: (BuildContext context, GoRouterState state) =>
  154. const DetailsPlaceholderScreen(
  155. label: 'Search Page details',
  156. ),
  157. ),
  158. ],
  159. ),
  160. ],
  161. ),
  162. StatefulShellBranch(
  163. routes: <RouteBase>[
  164. GoRoute(
  165. path: '/e',
  166. builder: (BuildContext context, GoRouterState state) =>
  167. const RootPlaceholderScreen(
  168. label: 'Notification',
  169. detailsPath: '/e/details',
  170. ),
  171. routes: <RouteBase>[
  172. GoRoute(
  173. path: 'details',
  174. builder: (BuildContext context, GoRouterState state) =>
  175. const DetailsPlaceholderScreen(
  176. label: 'Notification Page details',
  177. ),
  178. ),
  179. ],
  180. ),
  181. ],
  182. ),
  183. ],
  184. );
  185. }
  186. GoRoute _desktopHomeScreenRoute() {
  187. return GoRoute(
  188. path: DesktopHomeScreen.routeName,
  189. pageBuilder: (context, state) {
  190. return CustomTransitionPage(
  191. child: const DesktopHomeScreen(),
  192. transitionsBuilder: _buildFadeTransition,
  193. transitionDuration: _slowDuration,
  194. );
  195. },
  196. );
  197. }
  198. GoRoute _workspaceErrorScreenRoute() {
  199. return GoRoute(
  200. path: WorkspaceErrorScreen.routeName,
  201. pageBuilder: (context, state) {
  202. final args = state.extra as Map<String, dynamic>;
  203. return CustomTransitionPage(
  204. child: WorkspaceErrorScreen(
  205. error: args[WorkspaceErrorScreen.argError],
  206. userFolder: args[WorkspaceErrorScreen.argUserFolder],
  207. ),
  208. transitionsBuilder: _buildFadeTransition,
  209. transitionDuration: _slowDuration,
  210. );
  211. },
  212. );
  213. }
  214. GoRoute _encryptSecretScreenRoute() {
  215. return GoRoute(
  216. path: EncryptSecretScreen.routeName,
  217. pageBuilder: (context, state) {
  218. final args = state.extra as Map<String, dynamic>;
  219. return CustomTransitionPage(
  220. child: EncryptSecretScreen(
  221. user: args[EncryptSecretScreen.argUser],
  222. key: args[EncryptSecretScreen.argKey],
  223. ),
  224. transitionsBuilder: _buildFadeTransition,
  225. transitionDuration: _slowDuration,
  226. );
  227. },
  228. );
  229. }
  230. GoRoute _skipLogInScreenRoute() {
  231. return GoRoute(
  232. path: SkipLogInScreen.routeName,
  233. pageBuilder: (context, state) {
  234. return CustomTransitionPage(
  235. child: const SkipLogInScreen(),
  236. transitionsBuilder: _buildFadeTransition,
  237. transitionDuration: _slowDuration,
  238. );
  239. },
  240. );
  241. }
  242. GoRoute _signInScreenRoute() {
  243. return GoRoute(
  244. path: SignInScreen.routeName,
  245. pageBuilder: (context, state) {
  246. return CustomTransitionPage(
  247. child: const SignInScreen(),
  248. transitionsBuilder: _buildFadeTransition,
  249. transitionDuration: _slowDuration,
  250. );
  251. },
  252. );
  253. }
  254. GoRoute _rootRoute(Widget child) {
  255. return GoRoute(
  256. path: '/',
  257. redirect: (context, state) async {
  258. // Every time before navigating to splash screen, we check if user is already logged in in desktop. It is used to skip showing splash screen when user just changes apperance settings like theme mode.
  259. final userResponse = await getIt<AuthService>().getUser();
  260. final routeName = userResponse.fold(
  261. (error) => null,
  262. (user) => DesktopHomeScreen.routeName,
  263. );
  264. if (routeName != null && !PlatformExtension.isMobile) return routeName;
  265. return null;
  266. },
  267. // Root route is SplashScreen.
  268. // It needs LaunchConfiguration as a parameter, so we get it from ApplicationWidget's child.
  269. pageBuilder: (context, state) => MaterialPage(
  270. child: child,
  271. ),
  272. );
  273. }
  274. Widget _buildFadeTransition(
  275. BuildContext context,
  276. Animation<double> animation,
  277. Animation<double> secondaryAnimation,
  278. Widget child,
  279. ) =>
  280. FadeTransition(opacity: animation, child: child);
  281. Duration _slowDuration = Duration(
  282. milliseconds: (RouteDurations.slow.inMilliseconds).round(),
  283. );