import 'dart:io'; import 'dart:math'; import 'dart:ui'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; class Device { static Locale? locale; AndroidDeviceInfo? androidDeviceInfo; final Directory baseDir; Device(this.context, this.baseDir); // ✅ 判断是否32位设备(安全访问) bool get is32BitDevice { if (Platform.isIOS) return false; final info = androidDeviceInfo; if (info == null) return false; // 默认假设64位 return !info.supportedAbis.any((abi) => abi.contains('64')); } /// 获取平台性能(安全默认值) int get androidSdkInt { final info = androidDeviceInfo; return info?.version.sdkInt ?? 100; // 默认假设高版本 } bool get isOldAndroid => androidSdkInt < 26; bool get isLowRamDevice { final info = androidDeviceInfo; return info?.isLowRamDevice ?? false; // 默认假设不是 } bool get lowCpu => Platform.numberOfProcessors <= 2; /// ✅ 综合判断是否低端设备(即使 androidDeviceInfo 为 null 也能工作) bool get isLowEndDevice { if (Platform.isIOS) return false; // 基于 CPU 的快速判断(不依赖 androidDeviceInfo) if (lowCpu) return true; // 如果设备信息已加载,进行详细判断 final info = androidDeviceInfo; if (info != null) { return isOldAndroid || isLowRamDevice || is32BitDevice; } // 默认假设不是低端设备 return false; } static double get devPixelRatio => PlatformDispatcher.instance.views.first.devicePixelRatio; static Size get physicalSize => PlatformDispatcher.instance.views.first.physicalSize; static Size get logicalSize => physicalSize / devPixelRatio; ///final Size screenSize; final BuildContext context; final aspectRatio = 3.0 / 2.0; // 图片宽高比按照 2 / 3 设定,相应的 board play 区域也是这个比例 final _tabletProfile = DeviceProfile(horizontalPadding: 60, verticalPadding: 10); final _profile = DeviceProfile(horizontalPadding: 10, verticalPadding: 10); double _bannerHeight = 0; set bannerHeight(double height) { _bannerHeight = height; } /// 获取当前配置 DeviceProfile get profile => isTablet ? _tabletProfile : _profile; /// 屏幕尺寸 Size get screenSize => MediaQuery.of(context).size; // 真实像素密度 double get realPixelRatio => devPixelRatio; /// 像素密度 /// 像素密度:低端机通过降低采样倍率来保护内存 /// 这里的逻辑是:高端机用真实DPR,低端机降级,但不直接降到1(除非设备极差) double get effectivePixelRatio { double realDPR = PlatformDispatcher.instance.views.first.devicePixelRatio; if (isLowEndDevice) { return (realDPR > 2.0) ? 1.5 : 1.0; // 适当降级,保留一点清晰度 } return realDPR; } /// ✅ 建议的图片质量(优先基于屏幕尺寸,避免依赖 androidDeviceInfo) String get suggestedQuality { if (isTablet) return "2400"; if (isLowEndDevice) return "1200"; return "1800"; } /// safeArea高度 Z double get safeAreaHeight => MediaQuery.of(context).viewPadding.top + MediaQuery.of(context).viewPadding.bottom; /// 获取appbar的高度 double get appBarHeight => AppBar().preferredSize.height; /// Play 尺寸 Size get boardSize => Size(screenSize.width - profile.horizontalPadding * 2, screenSize.height - (safeAreaHeight + appBarHeight + profile.verticalPadding * 2)); /// 根据谷歌的描述,banner尺寸最小50px, 最大不超过15% double get estimateBannerHeight => max(50, screenSize.height * 0.15).truncateToDouble(); // double get bannerHeight => _bannerHeight == 0 ? estimateBannerHeight : _bannerHeight; double get bannerHeight => _bannerHeight != 0 ? _bannerHeight : isTablet ? 90 : 50; /// 是否平板 bool get isTablet => screenSize.shortestSide >= 600; String filePath(String relativePath) => '${baseDir.path}/$relativePath'; // board核心绘制区域 Rect get targetRect { final double availableHeight = screenSize.height - appBarHeight - bannerHeight - 10; // -10 是预留给banner广告的间隔距离 final double paddedWidth = screenSize.width - 2 * profile.horizontalPadding; final double paddedHeight = availableHeight - 2 * profile.verticalPadding; final double targetWidth = paddedWidth; final double targetHeight = targetWidth * aspectRatio; final double finalPuzzleWidth; final double finalPuzzleHeight; if (targetHeight > paddedHeight) { finalPuzzleHeight = paddedHeight; finalPuzzleWidth = paddedHeight / aspectRatio; } else { finalPuzzleWidth = targetWidth; finalPuzzleHeight = targetHeight; } final double targetYStart = appBarHeight + profile.verticalPadding + (paddedHeight - finalPuzzleHeight) / 2; final double targetXStart = profile.horizontalPadding + (paddedWidth - finalPuzzleWidth) / 2; final newTargetRect = Rect.fromLTWH(targetXStart, targetYStart, finalPuzzleWidth, finalPuzzleHeight); return newTargetRect; } // 最佳图片分辨率 Size get bestImageSize => Size(targetRect.width * effectivePixelRatio, targetRect.height * effectivePixelRatio); } class DeviceProfile { final double horizontalPadding; // 水平padding final double verticalPadding; // 垂直padding DeviceProfile({ this.horizontalPadding = 10, this.verticalPadding = 10, //Play board padding }); }