Files

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),
);
}