Files
fin-tracker/app/lib/router.dart
T
Dmitry 521924b3a4 fix(app): перезагрузка страницы возвращает на открытый экран, а не на главную
Пока сессия восстанавливается, адрес запоминается в /login?from=…, после входа роутер возвращает на него. Принимаются только пути внутри приложения.
2026-09-20 10:46:40 +03:00

128 lines
5.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'core/auth/auth_controller.dart';
import 'features/accounts/accounts_page.dart';
import 'features/cashflow/cashflow_page.dart';
import 'features/categories/categories_page.dart';
import 'features/events/events_page.dart';
import 'features/goals/goals_page.dart';
import 'features/health/health_page.dart';
import 'features/home/home_page.dart';
import 'features/income/income_page.dart';
import 'features/imports/import_preview_page.dart';
import 'features/imports/imports_page.dart';
import 'features/login/login_page.dart';
import 'features/pending/pending_page.dart';
import 'features/portfolio/instrument_page.dart';
import 'features/portfolio/portfolio_page.dart';
import 'features/portfolios/portfolios_page.dart';
import 'features/rebalance/rebalance_page.dart';
import 'features/rules/rules_page.dart';
import 'features/settings/settings_page.dart';
import 'features/shell/analytics_page.dart';
import 'features/shell/app_shell.dart';
import 'features/tax/tax_page.dart';
import 'features/transactions/transactions_page.dart';
/// Where to go after signing in: the page the user was on, if `from` is an in-app path.
String _returnTo(String? from) {
final inApp = from != null && from.startsWith('/') && !from.startsWith('//');
return inApp && !from.startsWith('/login') ? from : '/';
}
final routerProvider = Provider<GoRouter>((ref) {
final auth = ValueNotifier<AuthState>(ref.read(authControllerProvider));
ref.listen(authControllerProvider, (_, next) => auth.value = next);
ref.onDispose(auth.dispose);
return GoRouter(
initialLocation: '/',
refreshListenable: auth,
redirect: (context, state) {
final status = auth.value.status;
final atLogin = state.matchedLocation == '/login';
return switch (status) {
// A reload starts here while the stored session is still being restored: the address
// is kept in `from` so signing back in lands on it instead of on the home page. A
// deliberate sign-out is `signedOut` from the start and carries no `from`.
AuthStatus.unknown =>
atLogin
? null
: Uri(
path: '/login',
queryParameters: {'from': state.uri.toString()},
).toString(),
AuthStatus.signedOut => atLogin ? null : '/login',
AuthStatus.signedIn =>
atLogin ? _returnTo(state.uri.queryParameters['from']) : null,
};
},
routes: [
GoRoute(path: '/login', builder: (_, _) => const LoginPage()),
ShellRoute(
builder: (context, state, child) =>
AppShell(location: state.matchedLocation, child: child),
routes: [
GoRoute(path: '/', builder: (_, _) => const HomePage()),
GoRoute(path: '/accounts', builder: (_, _) => const AccountsPage()),
GoRoute(path: '/portfolio', builder: (_, _) => const PortfolioPage()),
GoRoute(
path: '/portfolio/instrument/:id',
builder: (_, state) => InstrumentPage(
instrumentId: int.parse(state.pathParameters['id']!),
),
),
// Аналитика: one navigation destination, four contract routes. The hub is what
// the shell points at; each screen below keeps its own top-level path and stays
// deep-linkable (see `alsoMatches` in `app_shell.dart`).
GoRoute(
path: '/analytics',
builder: (_, _) => const AnalyticsHubPage(),
),
GoRoute(path: '/income', builder: (_, _) => const IncomePage()),
GoRoute(
path: '/portfolios',
builder: (_, _) => const PortfoliosPage(),
),
GoRoute(path: '/rebalance', builder: (_, _) => const RebalancePage()),
GoRoute(path: '/goals', builder: (_, _) => const GoalsPage()),
GoRoute(path: '/tax', builder: (_, _) => const TaxPage()),
GoRoute(path: '/events', builder: (_, _) => const EventsPage()),
GoRoute(path: '/imports', builder: (_, _) => const ImportsPage()),
GoRoute(
path: '/imports/:id',
builder: (_, state) => ImportPreviewPage(
importId: int.parse(state.pathParameters['id']!),
),
),
GoRoute(
path: '/instruments/pending',
builder: (_, _) => const PendingInstrumentsPage(),
),
GoRoute(path: '/cashflow', builder: (_, _) => const CashflowPage()),
GoRoute(
path: '/categories',
builder: (_, state) => CategoriesPage(
initialMonth: state.uri.queryParameters['month'],
),
),
GoRoute(
path: '/transactions',
builder: (_, _) => const TransactionsPage(),
),
GoRoute(path: '/rules', builder: (_, _) => const RulesPage()),
GoRoute(
path: '/health',
builder: (_, state) => HealthPage(
initialTab: state.uri.queryParameters['tab'] == 'quality' ? 1 : 0,
),
),
GoRoute(path: '/settings', builder: (_, _) => const SettingsPage()),
],
),
],
);
});