import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class _Destination { const _Destination(this.path, this.icon, this.selectedIcon, this.label); final String path; final IconData icon; final IconData selectedIcon; final String label; } const _destinations = [ _Destination('/', Icons.dashboard_outlined, Icons.dashboard, 'Обзор'), _Destination('/accounts', Icons.account_balance_outlined, Icons.account_balance, 'Счета'), _Destination('/portfolio', Icons.pie_chart_outline, Icons.pie_chart, 'Портфель'), _Destination('/events', Icons.event_note_outlined, Icons.event_note, 'События'), _Destination('/cashflow', Icons.swap_horiz_outlined, Icons.swap_horiz, 'Потоки'), _Destination('/categories', Icons.category_outlined, Icons.category, 'Категории'), _Destination( '/transactions', Icons.receipt_long_outlined, Icons.receipt_long, 'Операции'), _Destination('/rules', Icons.rule_folder_outlined, Icons.rule_folder, 'Правила'), _Destination('/sync', Icons.sync_outlined, Icons.sync, 'Синк'), _Destination('/settings', Icons.settings_outlined, Icons.settings, 'Настройки'), ]; /// Breakpoints per the plan: bottom bar under 600, collapsed rail 600–1200, /// extended rail at 1200 and above. const _narrowBreakpoint = 600.0; const _wideBreakpoint = 1200.0; /// Adaptive navigation shell around the current route: a bottom /// [NavigationBar] on narrow surfaces, a [NavigationRail] (collapsed or /// extended) otherwise. class AppShell extends StatelessWidget { const AppShell({required this.location, required this.child, super.key}); final String location; final Widget child; int get _selectedIndex { // longest prefix wins, so /portfolio/instrument/311 keeps Портфель selected var best = -1; for (var i = 0; i < _destinations.length; i++) { final path = _destinations[i].path; final matches = path == '/' ? location == '/' : location.startsWith(path); if (matches && (best == -1 || path.length > _destinations[best].path.length)) best = i; } return best == -1 ? 0 : best; } void _onSelect(BuildContext context, int index) { if (index != _selectedIndex) context.go(_destinations[index].path); } @override Widget build(BuildContext context) { final width = MediaQuery.sizeOf(context).width; if (width < _narrowBreakpoint) { return Scaffold( body: SafeArea(child: child), bottomNavigationBar: NavigationBar( selectedIndex: _selectedIndex, onDestinationSelected: (i) => _onSelect(context, i), destinations: [ for (final d in _destinations) NavigationDestination( icon: Icon(d.icon), selectedIcon: Icon(d.selectedIcon), label: d.label, ), ], ), ); } final extended = width >= _wideBreakpoint; return Scaffold( body: Row( children: [ NavigationRail( extended: extended, minExtendedWidth: 220, selectedIndex: _selectedIndex, onDestinationSelected: (i) => _onSelect(context, i), labelType: extended ? NavigationRailLabelType.none : NavigationRailLabelType.selected, destinations: [ for (final d in _destinations) NavigationRailDestination( icon: Icon(d.icon), selectedIcon: Icon(d.selectedIcon), label: Text(d.label), ), ], ), const VerticalDivider(width: 1), Expanded(child: SafeArea(child: child)), ], ), ); } }