Files
submanager/lib/widgets/app_navigator.dart
JiWoong Sul 4731288622 Major UI/UX and architecture improvements
- Implemented new navigation system with NavigationProvider and route management
- Added adaptive theme system with ThemeProvider for better theme handling
- Introduced glassmorphism design elements (app bars, scaffolds, cards)
- Added advanced animations (spring animations, page transitions, staggered lists)
- Implemented performance optimizations (memory manager, lazy loading)
- Refactored Analysis screen into modular components
- Added floating navigation bar with haptic feedback
- Improved subscription cards with swipe actions
- Enhanced skeleton loading with better animations
- Added cached network image support
- Improved overall app architecture and code organization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-10 18:36:57 +09:00

200 lines
6.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../screens/main_screen.dart';
import '../screens/analysis_screen.dart';
import '../screens/add_subscription_screen.dart';
import '../screens/detail_screen.dart';
import '../screens/settings_screen.dart';
import '../screens/sms_scan_screen.dart';
import '../screens/category_management_screen.dart';
import '../screens/app_lock_screen.dart';
import '../models/subscription_model.dart';
import '../providers/navigation_provider.dart';
import '../routes/app_routes.dart';
import 'animated_page_transitions.dart';
/// 앱 전체의 네비게이션을 관리하는 클래스
class AppNavigator {
// NavigationProvider를 사용하여 상태를 관리하므로 더 이상 싱글톤 패턴이 필요하지 않음
/// 홈으로 네비게이션
static Future<void> toHome(BuildContext context) async {
HapticFeedback.lightImpact();
final navigationProvider = context.read<NavigationProvider>();
navigationProvider.clearHistoryAndGoHome();
await Navigator.of(context).pushNamedAndRemoveUntil(
AppRoutes.main,
(route) => false,
);
}
/// 분석 화면으로 네비게이션
static Future<void> toAnalysis(BuildContext context) async {
HapticFeedback.lightImpact();
final navigationProvider = context.read<NavigationProvider>();
navigationProvider.updateCurrentIndex(1);
await Navigator.of(context).pushNamed(AppRoutes.analysis);
}
/// 구독 추가 화면으로 네비게이션
static Future<void> toAddSubscription(BuildContext context) async {
HapticFeedback.mediumImpact();
await Navigator.of(context).pushNamed(AppRoutes.addSubscription);
}
/// 구독 상세 화면으로 네비게이션
static Future<void> toDetail(BuildContext context, SubscriptionModel subscription) async {
HapticFeedback.lightImpact();
await Navigator.of(context).pushNamed(
AppRoutes.subscriptionDetail,
arguments: subscription,
);
}
/// SMS 스캔 화면으로 네비게이션
static Future<void> toSmsScan(BuildContext context) async {
HapticFeedback.lightImpact();
final navigationProvider = context.read<NavigationProvider>();
navigationProvider.updateCurrentIndex(3);
await Navigator.of(context).pushNamed(AppRoutes.smsScanner);
}
/// 설정 화면으로 네비게이션
static Future<void> toSettings(BuildContext context) async {
HapticFeedback.lightImpact();
final navigationProvider = context.read<NavigationProvider>();
navigationProvider.updateCurrentIndex(4);
await Navigator.of(context).pushNamed(AppRoutes.settings);
}
/// 카테고리 관리 화면으로 네비게이션
static Future<void> toCategoryManagement(BuildContext context) async {
HapticFeedback.lightImpact();
await Navigator.of(context).push(
SlidePageRoute(
page: const CategoryManagementScreen(),
direction: AxisDirection.up,
),
);
}
/// 앱 잠금 화면으로 네비게이션
static Future<void> toAppLock(BuildContext context) async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const AppLockScreen(),
fullscreenDialog: true,
),
);
}
/// 뒤로가기 처리
static Future<bool> handleBackButton(BuildContext context) async {
final navigator = Navigator.of(context);
final navigationProvider = context.read<NavigationProvider>();
// 네비게이션 스택이 있으면 팝
if (navigator.canPop()) {
HapticFeedback.lightImpact();
// NavigationProvider의 히스토리를 사용하여 이전 인덱스로 복원
if (navigationProvider.canPop()) {
navigationProvider.pop();
}
navigator.pop();
return false;
}
// 앱 종료 확인
final shouldExit = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('앱 종료'),
content: const Text('SubManager를 종료하시겠습니까?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('취소'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('종료'),
),
],
),
);
return shouldExit ?? false;
}
/// 플로팅 네비게이션 바 탭 처리
static void handleFloatingNavTap(BuildContext context, int index) {
final navigationProvider = context.read<NavigationProvider>();
final currentIndex = navigationProvider.currentIndex;
// 같은 탭을 다시 탭하면 아무 동작 안 함
if (currentIndex == index) {
return;
}
// 현재 화면이 메인이 아니면 먼저 메인으로 돌아가기
if (Navigator.of(context).canPop()) {
Navigator.of(context).popUntil((route) => route.isFirst);
}
// 선택된 인덱스에 따라 네비게이션
switch (index) {
case 0: // 홈
navigationProvider.updateCurrentIndex(0);
break;
case 1: // 분석
toAnalysis(context);
break;
case 2: // 추가
toAddSubscription(context);
break;
case 3: // SMS
toSmsScan(context);
break;
case 4: // 설정
toSettings(context);
break;
}
}
}
/// 네비게이션 관찰자 (디버깅용)
class AppNavigationObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPush(route, previousRoute);
debugPrint('Navigation: Push ${route.settings.name}');
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);
debugPrint('Navigation: Pop ${route.settings.name}');
}
@override
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didRemove(route, previousRoute);
debugPrint('Navigation: Remove ${route.settings.name}');
}
@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
debugPrint('Navigation: Replace ${oldRoute?.settings.name} with ${newRoute?.settings.name}');
}
}