import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../features/assets/presentation/asset_add_page.dart'; import '../../features/assets/presentation/asset_edit_page.dart'; import '../../features/assets/presentation/assets_page.dart'; import '../../features/assets/presentation/holding_detail_page.dart'; import '../../features/auth/presentation/delete_account_page.dart'; import '../../features/auth/presentation/login_page.dart'; import '../../features/debug/presentation/debug_info_page.dart'; import '../../features/debug/presentation/debug_page.dart'; import '../../features/debug/presentation/debug_shorebird_page.dart'; import '../../features/debug/presentation/fancy_dio_inspector_page.dart'; import '../../features/debug/presentation/typography_test_page.dart'; import '../../features/market/presentation/market_page.dart'; import '../../features/market/presentation/market_kline_page.dart'; import '../../features/news/presentation/news_page.dart'; import '../../features/news/presentation/news_detail_page.dart'; import '../../features/profile/presentation/profile_page.dart'; import '../../features/profile/presentation/profile_action_pages.dart'; import '../../features/settings/presentation/settings_page.dart'; import '../../features/settings/presentation/privacy_consent_page.dart'; import '../services/device_header_service.dart'; import 'tab_shell.dart'; abstract final class AppRoutes { static const assets = '/assets'; static const assetAdd = '/assets/add'; static const assetEdit = '/assets/edit'; static const market = '/market'; static const marketKline = '/market/kline'; static const news = '/news'; static const newsDetail = '/news/detail'; static const profile = '/profile'; static const profileInvite = '/profile/invite'; static const profileShare = '/profile/share'; static const profileLocalBackup = '/profile/local-backup'; static const profileCloudBackup = '/profile/cloud-backup'; static const profileWidget = '/profile/widget'; static const profilePurity = '/profile/purity'; static const profileRecycle = '/profile/recycle'; static const profilePriceColor = '/profile/price-color'; static const profileCurrency = '/profile/currency'; static const profileDisclaimer = '/profile/disclaimer'; static const profileAbout = '/profile/about'; static const assetDetail = '/assets/detail'; static const login = '/login'; static const settings = '/settings'; static const deleteAccount = '/delete-account'; static const deleteAccountCompleted = '/delete-account-completed'; static const debug = '/debug'; static const debugInfo = '/debug/info'; static const debugShorebird = '/debug/shorebird'; static const debugTypography = '/debug/typography'; static const fancyDioInspector = '/debug/fancy_dio_inspector'; static const privacyConsent = '/privacy-consent'; } final routerProvider = Provider((ref) { final startupListenable = ValueNotifier(0); return GoRouter( initialLocation: AppRoutes.assets, refreshListenable: startupListenable, redirect: (context, state) { final loc = state.matchedLocation; final onConsent = loc == AppRoutes.privacyConsent; final showStartup = DeviceHeaderService.to.privacyConsentRequired || DeviceHeaderService.to.iosStartupRequired; if (showStartup) { if (!onConsent) return AppRoutes.privacyConsent; } else if (onConsent) { return AppRoutes.assets; } return null; }, routes: [ ShellRoute( builder: (context, state, child) => TabShell(location: state.matchedLocation, child: child), routes: [ GoRoute( path: AppRoutes.assets, builder: (ctx, s) => const AssetsPage(), ), GoRoute( path: AppRoutes.market, builder: (ctx, s) => const MarketPage(), ), GoRoute(path: AppRoutes.news, builder: (ctx, s) => const NewsPage()), GoRoute( path: AppRoutes.profile, builder: (ctx, s) => const ProfilePage(), ), ], ), GoRoute( path: AppRoutes.assetDetail, builder: (ctx, s) => HoldingDetailPage(id: s.uri.queryParameters['id']), ), GoRoute( path: AppRoutes.assetAdd, pageBuilder: (ctx, s) => _rightToLeftPage(state: s, child: const AssetAddPage()), ), GoRoute( path: AppRoutes.assetEdit, pageBuilder: (ctx, s) => _rightToLeftPage( state: s, child: AssetEditPage(id: s.uri.queryParameters['id'] ?? ''), ), ), GoRoute( path: AppRoutes.marketKline, builder: (ctx, s) => MarketKlinePage( metalId: s.uri.queryParameters['metal'], periodId: s.uri.queryParameters['period'], ), ), GoRoute( path: AppRoutes.newsDetail, builder: (ctx, s) => NewsDetailPage(id: s.uri.queryParameters['id']), ), GoRoute( path: AppRoutes.profileInvite, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const InviteRewardPage()), ), GoRoute( path: AppRoutes.profileShare, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const ShareCardPage()), ), GoRoute( path: AppRoutes.profileLocalBackup, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const LocalBackupPage()), ), GoRoute( path: AppRoutes.profileCloudBackup, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const CloudBackupPage()), ), GoRoute( path: AppRoutes.profileWidget, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const WidgetSettingsPage()), ), GoRoute( path: AppRoutes.profilePurity, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const PuritySettingsPage()), ), GoRoute( path: AppRoutes.profileRecycle, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const RecycleDiscountPage()), ), GoRoute( path: AppRoutes.profilePriceColor, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const PriceColorPage()), ), GoRoute( path: AppRoutes.profileCurrency, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const CurrencyPage()), ), GoRoute( path: AppRoutes.profileDisclaimer, pageBuilder: (ctx, s) => _rightToLeftPage(state: s, child: const DisclaimerPage()), ), GoRoute( path: AppRoutes.profileAbout, pageBuilder: (ctx, s) => _bottomUpPage(state: s, child: const AboutPage()), ), GoRoute(path: AppRoutes.debug, builder: (_, s) => const DebugPage()), GoRoute( path: AppRoutes.login, builder: (_, s) => LoginPage(next: s.uri.queryParameters['next']), ), GoRoute( path: AppRoutes.settings, builder: (_, s) => const SettingsPage(), ), GoRoute( path: AppRoutes.deleteAccount, builder: (_, s) => const DeleteAccountPage(), ), GoRoute( path: AppRoutes.deleteAccountCompleted, builder: (_, s) => const DeleteAccountSuccessPage(), ), GoRoute( path: AppRoutes.debugInfo, builder: (_, s) => const DebugInfoPage(), ), GoRoute( path: AppRoutes.debugShorebird, builder: (_, s) => const DebugShorebirdPage(), ), GoRoute( path: AppRoutes.debugTypography, builder: (_, s) => const TypographyTestPage(), ), GoRoute( path: AppRoutes.fancyDioInspector, builder: (_, s) => const FancyDioInspectorPage(), ), GoRoute( path: AppRoutes.privacyConsent, builder: (_, s) => const PrivacyConsentPage(), ), ], ); }); CustomTransitionPage _bottomUpPage({ required GoRouterState state, required Widget child, }) { return CustomTransitionPage( key: state.pageKey, transitionDuration: const Duration(milliseconds: 260), reverseTransitionDuration: const Duration(milliseconds: 220), child: child, transitionsBuilder: (context, animation, secondaryAnimation, pageChild) { final curved = CurvedAnimation( parent: animation, curve: Curves.easeOutCubic, reverseCurve: Curves.easeInCubic, ); return SlideTransition( position: Tween( begin: const Offset(0, 0.08), end: Offset.zero, ).animate(curved), child: FadeTransition(opacity: curved, child: pageChild), ); }, ); } CustomTransitionPage _rightToLeftPage({ required GoRouterState state, required Widget child, }) { return CustomTransitionPage( key: state.pageKey, transitionDuration: const Duration(milliseconds: 260), reverseTransitionDuration: const Duration(milliseconds: 220), child: child, transitionsBuilder: (context, animation, secondaryAnimation, pageChild) { final curved = CurvedAnimation( parent: animation, curve: Curves.easeOutCubic, reverseCurve: Curves.easeInCubic, ); return SlideTransition( position: Tween( begin: const Offset(1, 0), end: Offset.zero, ).animate(curved), child: FadeTransition(opacity: curved, child: pageChild), ); }, ); }