import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/cache/cached.dart'; import '../../core/widgets/stale_banner.dart'; import 'allocation_tab.dart'; import 'benchmarks_card.dart'; import 'holdings_tab.dart'; import 'providers.dart'; import 'scope_selector.dart'; /// Портфель: позиции и аллокация as two tabs of one screen, sharing one scope. /// /// They are tabs rather than two navigation destinations because they answer two halves of /// the same question, and because a tenth item in the bottom bar would leave 40 px per /// label on a phone. /// /// One offline banner covers both tabs (`docs/ai/offline-cache.md`) — `TabBarView` builds /// both eagerly anyway, so watching every provider here to compute it costs nothing extra. class PortfolioPage extends ConsumerWidget { const PortfolioPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final stale = oldestFetch([ ref.watch(portfolioSummaryProvider).valueOrNull?.fetchedAt, ref.watch(holdingsProvider).valueOrNull?.fetchedAt, ref.watch(valueSeriesProvider).valueOrNull?.fetchedAt, ref.watch(portfolioReturnsProvider).valueOrNull?.fetchedAt, ref.watch(benchmarkRowsProvider).valueOrNull?.fetchedAt, ref.watch(allocationProvider).valueOrNull?.fetchedAt, ]); return DefaultTabController( length: 2, child: Scaffold( appBar: AppBar( title: const Text('Портфель'), actions: const [ScopeSelector(), SizedBox(width: 8)], bottom: const TabBar( tabs: [ Tab(text: 'Позиции'), Tab(text: 'Аллокация'), ], ), ), body: Column( children: [ if (stale != null) Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), child: StaleBanner(fetchedAt: stale), ), const Expanded( child: TabBarView( children: [ HoldingsTab(sections: HoldingsSections.table), AllocationTab(), ], ), ), ], ), ), ); } }