| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- 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) {
- if (isLowEndDevice) {
- // 低端平板使用中等质量
- return "1800";
- }
- // 中高端平板使用更高质量
- return "2400";
- }
- // 手机设备:低端使用较低质量,其他使用默认质量
- if (isLowEndDevice) return "1200";
- // 根据实际像素比适度调整(高 DPR 的手机可以适当提高)
- final double dpr = devPixelRatio;
- if (dpr >= 3.0) return "2000";
- if (dpr >= 2.0) return "1800";
- return "1200";
- }
- /// 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
- });
- }
|