import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../core/constants/app_sections.dart'; class AppShell extends StatelessWidget { const AppShell({ super.key, required this.child, required this.currentLocation, }); final Widget child; final String currentLocation; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final isWide = constraints.maxWidth >= 960; if (isWide) { return Scaffold( appBar: AppBar( title: const Text('Superport v2'), actions: [ IconButton( tooltip: '로그아웃', icon: const Icon(LucideIcons.logOut), onPressed: () => context.go(loginRoutePath), ), ], ), body: Row( children: [ _NavigationRail(currentLocation: currentLocation), const VerticalDivider(width: 1), Expanded(child: child), ], ), ); } return Scaffold( appBar: AppBar( title: const Text('Superport v2'), actions: [ IconButton( tooltip: '로그아웃', icon: const Icon(LucideIcons.logOut), onPressed: () => context.go(loginRoutePath), ), ], ), drawer: Drawer( child: SafeArea( child: _NavigationList( currentLocation: currentLocation, onTap: (path) { Navigator.of(context).pop(); context.go(path); }, ), ), ), body: child, ); }, ); } } class _NavigationRail extends StatelessWidget { const _NavigationRail({required this.currentLocation}); final String currentLocation; @override Widget build(BuildContext context) { final pages = allAppPages; final selectedIndex = _selectedIndex(currentLocation, pages); final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Container( width: 104, decoration: BoxDecoration( border: Border(right: BorderSide(color: colorScheme.outlineVariant)), ), child: Column( children: [ const SizedBox(height: 24), const FlutterLogo(size: 48), const SizedBox(height: 24), Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), itemCount: pages.length, itemBuilder: (context, index) { final page = pages[index]; final isSelected = index == selectedIndex; final textStyle = theme.textTheme.labelSmall?.copyWith( color: isSelected ? colorScheme.primary : colorScheme.onSurfaceVariant, ); return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Material( color: isSelected ? colorScheme.primary.withValues(alpha: 0.12) : Colors.transparent, borderRadius: BorderRadius.circular(12), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { if (page.path != currentLocation) { context.go(page.path); } }, child: Padding( padding: const EdgeInsets.symmetric( vertical: 12, horizontal: 8, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( page.icon, size: 22, color: isSelected ? colorScheme.primary : colorScheme.onSurfaceVariant, ), const SizedBox(height: 6), Text( page.label, textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, style: textStyle, ), ], ), ), ), ), ); }, ), ), ], ), ); } } class _NavigationList extends StatelessWidget { const _NavigationList({required this.currentLocation, required this.onTap}); final String currentLocation; final ValueChanged onTap; @override Widget build(BuildContext context) { final pages = allAppPages; final selectedIndex = _selectedIndex(currentLocation, pages); return ListView.builder( itemCount: pages.length, itemBuilder: (context, index) { final page = pages[index]; final selected = index == selectedIndex; return ListTile( leading: Icon(page.icon), title: Text(page.label), subtitle: Text( page.summary, maxLines: 1, overflow: TextOverflow.ellipsis, ), selected: selected, selectedColor: Theme.of(context).colorScheme.primary, onTap: () => onTap(page.path), ); }, ); } } int _selectedIndex(String location, List pages) { final normalized = location.toLowerCase(); final exact = pages.indexWhere( (page) => normalized == page.path.toLowerCase(), ); if (exact != -1) { return exact; } final prefix = pages.indexWhere( (page) => normalized.startsWith(page.path.toLowerCase()), ); return prefix == -1 ? 0 : prefix; }