此提交包含多项变更: 1. 新增鸿蒙平台支持,完善设备检测与数据库适配 2. 替换旧版分享插件API为SharePlus 3. 批量迁移StateNotifier到Notifier以适配新版Riverpod 4. 修复zip编码判断、图表API参数等bug 5. 更新应用图标、启动页资源与多尺寸适配图标 6. 调整Android最小SDK版本与应用名称 7. 优化日志打印与正则表达式使用 8. 修正编辑器画布样式初始化与配置逻辑 9. 更新依赖与CI插件配置
270 lines
8.7 KiB
Dart
270 lines
8.7 KiB
Dart
// ============================================================
|
|
// 闲言APP — 跨平台设备检测 + 屏幕适配
|
|
// 创建时间: 2026-04-20
|
|
// 更新时间: 2026-04-23
|
|
// 作用: 检测平台/设备类型/屏幕方向/响应式断点,支持全局引用
|
|
// 上次更新: 增加横屏优先适配 + 响应式断点 + 全局引用入口
|
|
// ============================================================
|
|
|
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'platform_utils.dart' as pu;
|
|
|
|
// ============================================================
|
|
// 设备类型枚举
|
|
// ============================================================
|
|
|
|
enum DeviceType {
|
|
android('Android', true, false),
|
|
ios('iOS', true, false),
|
|
harmony('HarmonyOS', true, false),
|
|
windows('Windows', false, true),
|
|
macos('macOS', false, true),
|
|
linux('Linux', false, true),
|
|
web('Web', false, false),
|
|
unknown('Unknown', false, false);
|
|
|
|
const DeviceType(this.label, this.isMobile, this.isDesktop);
|
|
|
|
final String label;
|
|
final bool isMobile;
|
|
final bool isDesktop;
|
|
}
|
|
|
|
// ============================================================
|
|
// 屏幕方向枚举
|
|
// ============================================================
|
|
|
|
enum ScreenOrientation {
|
|
portrait,
|
|
landscape,
|
|
square;
|
|
|
|
bool get isLandscape => this == ScreenOrientation.landscape;
|
|
bool get isPortrait => this == ScreenOrientation.portrait;
|
|
}
|
|
|
|
// ============================================================
|
|
// 响应式断点 (与 design-rules.md 统一)
|
|
// ============================================================
|
|
|
|
class Breakpoints {
|
|
Breakpoints._();
|
|
|
|
static const double mobile = 640;
|
|
static const double tablet = 768;
|
|
static const double desktop = 1024;
|
|
static const double ultraWide = 1920;
|
|
|
|
static const double foldableMin = 600;
|
|
static const double foldableMax = 900;
|
|
}
|
|
|
|
// ============================================================
|
|
// 屏幕尺寸分类
|
|
// ============================================================
|
|
|
|
enum ScreenSizeClass {
|
|
compact('手机'),
|
|
foldable('折叠屏'),
|
|
medium('平板'),
|
|
expanded('桌面'),
|
|
ultraWide('大屏');
|
|
|
|
const ScreenSizeClass(this.label);
|
|
final String label;
|
|
|
|
bool get isCompact => this == ScreenSizeClass.compact;
|
|
bool get isFoldable => this == ScreenSizeClass.foldable;
|
|
bool get isMedium => this == ScreenSizeClass.medium;
|
|
bool get isExpanded => this == ScreenSizeClass.expanded;
|
|
bool get isUltraWide => this == ScreenSizeClass.ultraWide;
|
|
|
|
int get layoutColumns {
|
|
switch (this) {
|
|
case ScreenSizeClass.compact:
|
|
return 1;
|
|
case ScreenSizeClass.foldable:
|
|
return 2;
|
|
case ScreenSizeClass.medium:
|
|
return 2;
|
|
case ScreenSizeClass.expanded:
|
|
return 3;
|
|
case ScreenSizeClass.ultraWide:
|
|
return 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 设备检测工具类 — 全局引用入口
|
|
// ============================================================
|
|
|
|
class DeviceDetection {
|
|
DeviceDetection._();
|
|
|
|
static DeviceType get deviceType {
|
|
if (kIsWeb) return DeviceType.web;
|
|
try {
|
|
if (_isHarmonyOS()) return DeviceType.harmony;
|
|
if (pu.isAndroid) return DeviceType.android;
|
|
if (pu.isIOS) return DeviceType.ios;
|
|
if (pu.isWindows) return DeviceType.windows;
|
|
if (pu.isMacOS) return DeviceType.macos;
|
|
if (pu.isLinux) return DeviceType.linux;
|
|
} catch (_) {}
|
|
return DeviceType.unknown;
|
|
}
|
|
|
|
static bool get isMobile => deviceType.isMobile;
|
|
static bool get isDesktop => deviceType.isDesktop;
|
|
static bool get isAndroid => deviceType == DeviceType.android;
|
|
static bool get isIOS => deviceType == DeviceType.ios;
|
|
static bool get isHarmony => deviceType == DeviceType.harmony;
|
|
static bool get isWindows => deviceType == DeviceType.windows;
|
|
static bool get isMacOS => deviceType == DeviceType.macos;
|
|
static bool get isLinux => deviceType == DeviceType.linux;
|
|
static bool get isWeb => deviceType == DeviceType.web;
|
|
|
|
static String get platformName => deviceType.label;
|
|
|
|
static bool _isHarmonyOS() {
|
|
try {
|
|
if (pu.isWeb) return false;
|
|
return pu.isOhos;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 从 BuildContext 获取屏幕方向
|
|
static ScreenOrientation orientation(BuildContext context) {
|
|
final size = MediaQuery.sizeOf(context);
|
|
if (size.width > size.height) return ScreenOrientation.landscape;
|
|
if (size.width < size.height) return ScreenOrientation.portrait;
|
|
return ScreenOrientation.square;
|
|
}
|
|
|
|
/// 从 BuildContext 获取屏幕尺寸分类
|
|
static ScreenSizeClass sizeClass(BuildContext context) {
|
|
final width = MediaQuery.sizeOf(context).width;
|
|
return sizeClassFromWidth(width);
|
|
}
|
|
|
|
/// 从宽度值获取屏幕尺寸分类
|
|
static ScreenSizeClass sizeClassFromWidth(double width) {
|
|
if (width >= Breakpoints.ultraWide) return ScreenSizeClass.ultraWide;
|
|
if (width >= Breakpoints.desktop) return ScreenSizeClass.expanded;
|
|
if (width >= Breakpoints.tablet) return ScreenSizeClass.medium;
|
|
if (width >= Breakpoints.foldableMin && width <= Breakpoints.foldableMax) {
|
|
return ScreenSizeClass.foldable;
|
|
}
|
|
return ScreenSizeClass.compact;
|
|
}
|
|
|
|
/// 当前是否横屏
|
|
static bool isLandscape(BuildContext context) =>
|
|
orientation(context).isLandscape;
|
|
|
|
/// 当前是否竖屏
|
|
static bool isPortrait(BuildContext context) =>
|
|
orientation(context).isPortrait;
|
|
|
|
/// 当前是否折叠屏
|
|
static bool isFoldable(BuildContext context) => sizeClass(context).isFoldable;
|
|
|
|
/// 获取布局列数
|
|
static int layoutColumns(BuildContext context) =>
|
|
sizeClass(context).layoutColumns;
|
|
|
|
/// 横屏优先 — 获取主内容区最大宽度
|
|
static double contentMaxWidth(BuildContext context) {
|
|
final sizeClass = DeviceDetection.sizeClass(context);
|
|
switch (sizeClass) {
|
|
case ScreenSizeClass.compact:
|
|
return double.infinity;
|
|
case ScreenSizeClass.foldable:
|
|
return 900;
|
|
case ScreenSizeClass.medium:
|
|
return 1024;
|
|
case ScreenSizeClass.expanded:
|
|
return 1200;
|
|
case ScreenSizeClass.ultraWide:
|
|
return 1600;
|
|
}
|
|
}
|
|
|
|
/// 获取平台图标 emoji
|
|
static String get platformEmoji {
|
|
switch (deviceType) {
|
|
case DeviceType.android:
|
|
return '🤖';
|
|
case DeviceType.ios:
|
|
return '🍎';
|
|
case DeviceType.harmony:
|
|
return '🔴';
|
|
case DeviceType.windows:
|
|
return '🪟';
|
|
case DeviceType.macos:
|
|
return '💻';
|
|
case DeviceType.linux:
|
|
return '🐧';
|
|
case DeviceType.web:
|
|
return '🌐';
|
|
case DeviceType.unknown:
|
|
return '❓';
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 全局引用快捷方式 — 可在任何地方使用
|
|
// ============================================================
|
|
|
|
/// 全局设备检测快捷引用
|
|
/// 用法: `AppDevice.isIOS`, `AppDevice.isLandscape(context)`
|
|
class AppDevice {
|
|
AppDevice._();
|
|
|
|
static bool get isMobile => DeviceDetection.isMobile;
|
|
static bool get isDesktop => DeviceDetection.isDesktop;
|
|
static bool get isAndroid => DeviceDetection.isAndroid;
|
|
static bool get isIOS => DeviceDetection.isIOS;
|
|
static bool get isHarmony => DeviceDetection.isHarmony;
|
|
static bool get isWindows => DeviceDetection.isWindows;
|
|
static bool get isMacOS => DeviceDetection.isMacOS;
|
|
static bool get isLinux => DeviceDetection.isLinux;
|
|
static bool get isWeb => DeviceDetection.isWeb;
|
|
static String get platformName => DeviceDetection.platformName;
|
|
static String get platformEmoji => DeviceDetection.platformEmoji;
|
|
static DeviceType get platform => DeviceDetection.deviceType;
|
|
|
|
static ScreenOrientation orientation(BuildContext context) =>
|
|
DeviceDetection.orientation(context);
|
|
static ScreenSizeClass sizeClass(BuildContext context) =>
|
|
DeviceDetection.sizeClass(context);
|
|
static bool isLandscape(BuildContext context) =>
|
|
DeviceDetection.isLandscape(context);
|
|
static bool isPortrait(BuildContext context) =>
|
|
DeviceDetection.isPortrait(context);
|
|
static bool isFoldable(BuildContext context) =>
|
|
DeviceDetection.isFoldable(context);
|
|
static int layoutColumns(BuildContext context) =>
|
|
DeviceDetection.layoutColumns(context);
|
|
static double contentMaxWidth(BuildContext context) =>
|
|
DeviceDetection.contentMaxWidth(context);
|
|
}
|
|
|
|
/// 全局断点快捷引用
|
|
/// 用法: `AppBreakpoints.mobile`, `AppBreakpoints.desktop`
|
|
class AppBreakpoints {
|
|
AppBreakpoints._();
|
|
static const double mobile = Breakpoints.mobile;
|
|
static const double tablet = Breakpoints.tablet;
|
|
static const double desktop = Breakpoints.desktop;
|
|
static const double ultraWide = Breakpoints.ultraWide;
|
|
static const double foldableMin = Breakpoints.foldableMin;
|
|
static const double foldableMax = Breakpoints.foldableMax;
|
|
}
|