/// 메인 셸 - 툴바 + 바텀 네비게이션 library; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../core/constants.dart'; /// 메인 앱 셸 (툴바 + 바텀 네비게이션 + 콘텐츠) class MainShell extends StatelessWidget { final Widget child; const MainShell({super.key, required this.child}); @override Widget build(BuildContext context) { final location = GoRouterState.of(context).uri.path; final isMembersPage = location == '/members'; final isSchedulePage = location.startsWith('/schedule'); return Scaffold( backgroundColor: AppColors.background, // 앱바 (툴바) - 일정 페이지는 자체 툴바 사용, 멤버 페이지는 그림자 제거 appBar: isSchedulePage ? null : PreferredSize( preferredSize: const Size.fromHeight(56), child: Container( decoration: BoxDecoration( color: Colors.white, boxShadow: isMembersPage ? null : [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 4, offset: const Offset(0, 1), ), ], ), child: SafeArea( child: SizedBox( height: 56, child: Center( child: Text( _getTitle(location), style: const TextStyle( fontFamily: 'Pretendard', color: AppColors.primary, fontSize: 20, fontWeight: FontWeight.bold, ), ), ), ), ), ), ), // 콘텐츠 body: child, // 바텀 네비게이션 bottomNavigationBar: const _BottomNavBar(), ); } /// 현재 경로에 따른 타이틀 반환 String _getTitle(String location) { switch (location) { case '/': return 'fromis_9'; case '/members': return '멤버'; case '/album': return '앨범'; case '/schedule': return '일정'; default: return 'fromis_9'; } } } /// 바텀 네비게이션 바 class _BottomNavBar extends StatelessWidget { const _BottomNavBar(); @override Widget build(BuildContext context) { final location = GoRouterState.of(context).uri.path; return Container( decoration: const BoxDecoration( color: Colors.white, border: Border( top: BorderSide(color: AppColors.border, width: 1), ), ), child: SafeArea( child: SizedBox( height: 64, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _NavItem( iconName: 'home', label: '홈', isActive: location == '/', onTap: () => context.go('/'), ), _NavItem( iconName: 'users', label: '멤버', isActive: location == '/members', onTap: () => context.go('/members'), ), _NavItem( iconName: 'disc-3', label: '앨범', isActive: location.startsWith('/album'), onTap: () => context.go('/album'), ), _NavItem( iconName: 'calendar', label: '일정', isActive: location.startsWith('/schedule'), onTap: () => context.go('/schedule'), ), ], ), ), ), ); } } /// 네비게이션 아이템 - SVG 아이콘 사용으로 strokeWidth 조절 가능 class _NavItem extends StatelessWidget { final String iconName; final String label; final bool isActive; final VoidCallback onTap; const _NavItem({ required this.iconName, required this.label, required this.isActive, required this.onTap, }); /// SVG 아이콘 문자열 생성 (strokeWidth 동적 조절) String _getSvgString(String name, double strokeWidth) { const icons = { 'home': '', 'users': '', 'disc-3': '', 'calendar': '', }; return '''${icons[name]}'''; } @override Widget build(BuildContext context) { final color = isActive ? AppColors.primary : AppColors.textTertiary; // 웹과 동일: 활성화 시 strokeWidth=2.5, 비활성화 시 strokeWidth=2 final strokeWidth = isActive ? 2.5 : 2.0; return Expanded( child: InkWell( onTap: onTap, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 22, height: 22, child: SvgPicture.string( _getSvgString(iconName, strokeWidth), colorFilter: ColorFilter.mode(color, BlendMode.srcIn), ), ), const SizedBox(height: 4), Text( label, style: TextStyle( fontSize: 12, fontWeight: isActive ? FontWeight.w600 : FontWeight.w500, color: color, ), ), ], ), ), ); } }