import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'dart:async'; import 'dart:developer' as developer; /// 성능 최적화를 위한 유틸리티 클래스 class PerformanceOptimizer { static final PerformanceOptimizer _instance = PerformanceOptimizer._internal(); factory PerformanceOptimizer() => _instance; PerformanceOptimizer._internal(); // 프레임 타이밍 정보 final List _frameTimings = []; bool _isMonitoring = false; /// 프레임 성능 모니터링 시작 void startFrameMonitoring() { if (_isMonitoring) return; _isMonitoring = true; SchedulerBinding.instance.addTimingsCallback((timings) { _frameTimings.addAll(timings); // 최근 100개 프레임만 유지 if (_frameTimings.length > 100) { _frameTimings.removeRange(0, _frameTimings.length - 100); } }); } /// 프레임 성능 모니터링 중지 void stopFrameMonitoring() { if (!_isMonitoring) return; _isMonitoring = false; SchedulerBinding.instance.addTimingsCallback((_) {}); } /// 평균 FPS 계산 double getAverageFPS() { if (_frameTimings.isEmpty) return 0.0; double totalDuration = 0; for (final timing in _frameTimings) { totalDuration += timing.totalSpan.inMicroseconds; } final averageDuration = totalDuration / _frameTimings.length; return 1000000 / averageDuration; // microseconds to FPS } /// 메모리 사용량 모니터링 static Future getMemoryInfo() async { // Flutter에서는 직접적인 메모리 사용량 측정이 제한적이므로 // 이미지 캐시 사용량을 기준으로 측정 final imageCache = PaintingBinding.instance.imageCache; return MemoryInfo( currentUsage: imageCache.currentSizeBytes, capacity: imageCache.maximumSizeBytes, ); } /// 위젯 재빌드 최적화를 위한 데바운서 static Timer? _debounceTimer; static void debounce( VoidCallback callback, { Duration delay = const Duration(milliseconds: 300), }) { _debounceTimer?.cancel(); _debounceTimer = Timer(delay, callback); } /// 스로틀링 - 지정된 시간 간격으로만 실행 static DateTime? _lastThrottleTime; static void throttle( VoidCallback callback, { Duration interval = const Duration(milliseconds: 300), }) { final now = DateTime.now(); if (_lastThrottleTime == null || now.difference(_lastThrottleTime!) > interval) { _lastThrottleTime = now; callback(); } } /// 무거운 연산을 별도 Isolate에서 실행 static Future runInIsolate( ComputeCallback callback, dynamic parameter, ) async { return await compute(callback, parameter); } /// 레이지 로딩을 위한 페이지네이션 헬퍼 static List paginate({ required List items, required int page, required int pageSize, }) { final startIndex = page * pageSize; final endIndex = (startIndex + pageSize).clamp(0, items.length); if (startIndex >= items.length) return []; return items.sublist(startIndex, endIndex); } /// 이미지 최적화 - 메모리 효율적인 크기로 조정 static double getOptimalImageSize(BuildContext context, { required double originalSize, double maxSize = 1000, }) { final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; final screenSize = MediaQuery.of(context).size; final maxDimension = screenSize.width > screenSize.height ? screenSize.width : screenSize.height; final optimalSize = (maxDimension * devicePixelRatio).clamp(100.0, maxSize); return optimalSize < originalSize ? optimalSize : originalSize; } /// 위젯 키 최적화 static Key generateOptimizedKey(String prefix, dynamic identifier) { return ValueKey('${prefix}_$identifier'); } /// 애니메이션 최적화 - 보이지 않는 애니메이션 중지 static bool shouldAnimateWidget(BuildContext context) { final mediaQuery = MediaQuery.of(context); return !mediaQuery.disableAnimations && mediaQuery.accessibleNavigation; } /// 스크롤 성능 최적화 static ScrollPhysics getOptimizedScrollPhysics() { return const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ); } /// 빌드 최적화를 위한 const 위젯 권장사항 체크 static void checkConstOptimization() { if (kDebugMode) { print('💡 성능 최적화 팁:'); print('1. 가능한 모든 위젯에 const 사용'); print('2. StatelessWidget 대신 const 생성자 사용'); print('3. 큰 리스트는 ListView.builder 사용'); print('4. 이미지는 캐싱과 함께 적절한 크기로 로드'); print('5. 애니메이션은 AnimatedBuilder 사용'); } } /// 메모리 누수 감지 헬퍼 static final Map _widgetCounts = {}; static void trackWidget(String widgetName, bool isCreated) { if (!kDebugMode) return; _widgetCounts[widgetName] = (_widgetCounts[widgetName] ?? 0) + (isCreated ? 1 : -1); // 위젯이 비정상적으로 많이 생성되면 경고 if ((_widgetCounts[widgetName] ?? 0) > 100) { print('⚠️ 경고: $widgetName 위젯이 100개 이상 생성됨. 메모리 누수 가능성!'); } } } /// 메모리 정보 클래스 class MemoryInfo { final int currentUsage; final int capacity; MemoryInfo({ required this.currentUsage, required this.capacity, }); double get usagePercentage => (currentUsage / capacity) * 100; String get formattedUsage => '${(currentUsage / 1024 / 1024).toStringAsFixed(2)} MB'; String get formattedCapacity => '${(capacity / 1024 / 1024).toStringAsFixed(2)} MB'; } /// 성능 측정 데코레이터 class PerformanceMeasure { static Future measure({ required String name, required Future Function() operation, }) async { if (!kDebugMode) return await operation(); final stopwatch = Stopwatch()..start(); try { final result = await operation(); stopwatch.stop(); print('✅ $name 완료: ${stopwatch.elapsedMilliseconds}ms'); return result; } catch (e) { stopwatch.stop(); print('❌ $name 실패: ${stopwatch.elapsedMilliseconds}ms - $e'); rethrow; } } }