Files
fin-tracker/app/lib/router.dart
T
Dmitry c588b575a7 feat(app): экран «События»
Лента брокерского леджера: фильтры по счёту, типу, датам и поиск, бесконечная
прокрутка с явной кнопкой «Ещё», карточка события в bottom sheet с переходом на
инструмент. Паттерн контроллера и пагинации взят у транзакций один в один — это тот
же список с фильтрами, и второй способ делать одно и то же был бы просто вторым
способом его чинить.

Пункт навигации стоит между «Портфелем» и «Потоками», а не рядом с «Операциями»:
это инвестиционная лента, а в операциях лежит ZenMoney.

eventKindLabels переиспользован из portfolio/labels.dart, где он уже жил ради карточки
инструмента, а не скопирован: два словаря подписей для одного енума разъезжаются на
первом же новом типе события.

Бэкенд не менялся — /events с фильтрами и пагинацией закрывает экран целиком,
клиент не перегенерировался.
2026-09-18 15:08:12 +03:00

66 lines
2.7 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/home/home_page.dart';
import 'features/login/login_page.dart';
import 'features/portfolio/instrument_page.dart';
import 'features/portfolio/portfolio_page.dart';
import 'features/rules/rules_page.dart';
import 'features/settings/settings_page.dart';
import 'features/shell/app_shell.dart';
import 'features/sync/sync_page.dart';
import 'features/transactions/transactions_page.dart';
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) {
AuthStatus.unknown => atLogin ? null : '/login',
AuthStatus.signedOut => atLogin ? null : '/login',
AuthStatus.signedIn => atLogin ? '/' : 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']!)),
),
GoRoute(path: '/events', builder: (_, _) => const EventsPage()),
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: '/sync', builder: (_, _) => const SyncPage()),
GoRoute(path: '/settings', builder: (_, _) => const SettingsPage()),
],
),
],
);
});