204 lines
7.4 KiB
Dart
204 lines
7.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
|
import 'package:forui/forui.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/index.dart';
|
|
|
|
/// 主屏 Flutter 应用根组件,统一承载主题、路由、生命周期和用户活动监听。
|
|
class MainApp extends StatefulWidget {
|
|
/// 创建主屏应用根组件。
|
|
const MainApp({super.key});
|
|
|
|
/// 创建负责生命周期观察和根级空闲活动采集的应用状态。
|
|
@override
|
|
State<MainApp> createState() => _MainAppState();
|
|
}
|
|
|
|
/// 主屏应用生命周期与根视图状态。
|
|
class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
|
|
/// 注册应用生命周期观察者,供副屏、人脸和同步空闲状态共用。
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
/// 注销生命周期观察者,避免应用根组件释放后继续接收回调。
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
/// 同步前后台空闲状态,并在挂起时释放人脸会话、恢复时校验副屏窗口和补排部门人脸对账。
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
|
SyncIdleTool.instance.updateLifecycle(state);
|
|
if (state == AppLifecycleState.paused ||
|
|
state == AppLifecycleState.detached) {
|
|
if (Get.isRegistered<FaceRecognitionSessionService>()) {
|
|
try {
|
|
await Get.find<FaceRecognitionSessionService>().suspend();
|
|
} catch (_) {}
|
|
}
|
|
return;
|
|
}
|
|
if (state == AppLifecycleState.resumed) {
|
|
// 前台恢复需要重新确认原生 Presentation;若系统回收了副屏窗口才重新创建,
|
|
// 正常常驻时不会触发插件内部的“先关闭再打开”。
|
|
try {
|
|
await AppSubScreenTool.show(verifyPresentation: true);
|
|
} catch (_) {}
|
|
SyncCheckTool.instance.queueMissedDepartmentFaceSync();
|
|
}
|
|
}
|
|
|
|
/// 构建主题与 GetX 根应用,并在最外层 Listener 记录所有指针活动。
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ScreenUtilInit(
|
|
designSize: const Size(1920, 1080),
|
|
minTextAdapt: true,
|
|
splitScreenMode: true,
|
|
builder: (_, child) {
|
|
return Obx(() {
|
|
final appTheme = Get.find<AppThemeControl>();
|
|
final fTheme = appTheme.theme.value;
|
|
final sizeScalar = appTheme.sizeScalar.value;
|
|
final buttonRadius = appTheme.buttonRadius.value.radius.w;
|
|
final cardRadius = appTheme.cardRadius.value.radius.w;
|
|
final buttonSizeScale = appTheme.buttonSize.value.scale;
|
|
// 新 UI 保留用户字号缩放能力,只统一视觉色板与组件形态。
|
|
final colorScheme = fTheme.colorScheme.copyWith(
|
|
background: XfjDesignTokens.surface,
|
|
foreground: XfjDesignTokens.ink,
|
|
primary: XfjDesignTokens.primary,
|
|
primaryForeground: Colors.white,
|
|
secondary: XfjDesignTokens.surface,
|
|
secondaryForeground: XfjDesignTokens.ink,
|
|
muted: XfjDesignTokens.canvas,
|
|
mutedForeground: XfjDesignTokens.muted,
|
|
destructive: XfjDesignTokens.danger,
|
|
destructiveForeground: Colors.white,
|
|
error: XfjDesignTokens.danger,
|
|
errorForeground: Colors.white,
|
|
border: XfjDesignTokens.line,
|
|
);
|
|
final inheritedTheme = FThemeData.inherit(
|
|
colorScheme: colorScheme,
|
|
typography: fTheme.typography.scale(sizeScalar: sizeScalar),
|
|
style: fTheme.style.copyWith(
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(buttonRadius),
|
|
),
|
|
borderWidth: 1,
|
|
iconStyle: fTheme.style.iconStyle.copyWith(
|
|
size: fTheme.typography.xl2.fontSize,
|
|
),
|
|
pagePadding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 1.w),
|
|
),
|
|
);
|
|
final buttonPadding = EdgeInsets.symmetric(
|
|
horizontal: 16.w * buttonSizeScale,
|
|
vertical: 12.5.h * buttonSizeScale,
|
|
);
|
|
final iconPadding = EdgeInsets.all(7.5.w * buttonSizeScale);
|
|
final badgePadding = EdgeInsets.symmetric(
|
|
horizontal: 14.w * buttonSizeScale,
|
|
vertical: 2.h * buttonSizeScale,
|
|
);
|
|
final themeData = inheritedTheme.copyWith(
|
|
buttonStyles: _buttonStyles(
|
|
inheritedTheme.buttonStyles,
|
|
buttonPadding,
|
|
iconPadding,
|
|
),
|
|
badgeStyles: _badgeStyles(
|
|
inheritedTheme.badgeStyles,
|
|
buttonRadius,
|
|
badgePadding,
|
|
),
|
|
cardStyle: inheritedTheme.cardStyle.copyWith(
|
|
decoration: inheritedTheme.cardStyle.decoration.copyWith(
|
|
borderRadius: BorderRadius.circular(cardRadius),
|
|
),
|
|
),
|
|
);
|
|
return GetMaterialApp(
|
|
title: '收银机',
|
|
theme: ThemeData(
|
|
scaffoldBackgroundColor: XfjDesignTokens.canvas,
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: XfjDesignTokens.primary,
|
|
surface: XfjDesignTokens.surface,
|
|
),
|
|
bottomSheetTheme: const BottomSheetThemeData(
|
|
backgroundColor: Colors.transparent,
|
|
),
|
|
),
|
|
defaultTransition: Transition.noTransition,
|
|
initialRoute: RouteNames.load,
|
|
getPages: RoutePages.list,
|
|
enableLog: true,
|
|
debugShowCheckedModeBanner: false,
|
|
locale: const Locale('zh', 'CN'),
|
|
localizationsDelegates: FLocalizations.localizationsDelegates,
|
|
supportedLocales: FLocalizations.supportedLocales,
|
|
navigatorObservers: [
|
|
GlobalRouteObserver(),
|
|
],
|
|
builder: EasyLoading.init(
|
|
builder: (_, childSecond) => Listener(
|
|
behavior: HitTestBehavior.translucent,
|
|
onPointerDown: (_) =>
|
|
SyncIdleTool.instance.recordUserActivity(),
|
|
child: FTheme(
|
|
data: themeData,
|
|
child: childSecond!,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
FButtonStyles _buttonStyles(
|
|
FButtonStyles styles,
|
|
EdgeInsets padding,
|
|
EdgeInsets iconPadding,
|
|
) {
|
|
FButtonCustomStyle resize(FButtonCustomStyle style) => style.copyWith(
|
|
contentStyle: style.contentStyle.copyWith(padding: padding),
|
|
iconContentStyle: style.iconContentStyle.copyWith(padding: iconPadding),
|
|
);
|
|
return styles.copyWith(
|
|
primary: resize(styles.primary),
|
|
secondary: resize(styles.secondary),
|
|
destructive: resize(styles.destructive),
|
|
outline: resize(styles.outline),
|
|
ghost: resize(styles.ghost),
|
|
);
|
|
}
|
|
|
|
FBadgeStyles _badgeStyles(
|
|
FBadgeStyles styles,
|
|
double radius,
|
|
EdgeInsets padding,
|
|
) {
|
|
FBadgeCustomStyle resize(FBadgeCustomStyle style) => style.copyWith(
|
|
borderRadius: BorderRadius.circular(radius),
|
|
contentStyle: style.contentStyle.copyWith(padding: padding),
|
|
);
|
|
return styles.copyWith(
|
|
primary: resize(styles.primary),
|
|
secondary: resize(styles.secondary),
|
|
outline: resize(styles.outline),
|
|
destructive: resize(styles.destructive),
|
|
);
|
|
}
|