mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 10:03:43 +08:00
feat:riverpod 迁移 HomeGameDoctorUI
This commit is contained in:
parent
2aa4fb6c09
commit
c290e832a0
@ -23,6 +23,7 @@ import 'common/helper/system_helper.dart';
|
|||||||
import 'common/io/rs_http.dart';
|
import 'common/io/rs_http.dart';
|
||||||
import 'common/rust/frb_generated.dart';
|
import 'common/rust/frb_generated.dart';
|
||||||
import 'data/app_version_data.dart';
|
import 'data/app_version_data.dart';
|
||||||
|
import 'ui/home/game_doctor/game_doctor_ui.dart';
|
||||||
import 'ui/index_ui.dart';
|
import 'ui/index_ui.dart';
|
||||||
import 'ui/settings/upgrade_dialog.dart';
|
import 'ui/settings/upgrade_dialog.dart';
|
||||||
|
|
||||||
@ -43,6 +44,13 @@ GoRouter router(RouterRef ref) {
|
|||||||
path: '/index',
|
path: '/index',
|
||||||
pageBuilder: (context, state) =>
|
pageBuilder: (context, state) =>
|
||||||
myPageBuilder(context, state, const IndexUI()),
|
myPageBuilder(context, state, const IndexUI()),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'game_doctor',
|
||||||
|
pageBuilder: (context, state) =>
|
||||||
|
myPageBuilder(context, state, const HomeGameDoctorUI()),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ part of 'app.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$routerHash() => r'6e1ddfd4fb004ca0c2262ab17a4b0882f12bcc3b';
|
String _$routerHash() => r'd8cf08526e81cdcda0203c32fc89140f2ffb0058';
|
||||||
|
|
||||||
/// See also [router].
|
/// See also [router].
|
||||||
@ProviderFor(router)
|
@ProviderFor(router)
|
||||||
|
294
lib/ui/home/game_doctor/game_doctor_ui.dart
Normal file
294
lib/ui/home/game_doctor/game_doctor_ui.dart
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||||
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
import 'game_doctor_ui_model.dart';
|
||||||
|
|
||||||
|
class HomeGameDoctorUI extends HookConsumerWidget {
|
||||||
|
const HomeGameDoctorUI({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final state = ref.watch(homeGameDoctorUIModelProvider);
|
||||||
|
final homeState = ref.watch(homeUIModelProvider);
|
||||||
|
final model = ref.read(homeGameDoctorUIModelProvider.notifier);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
dPrint("HomeGameDoctorUI useEffect doCheck timeStamp === $timeStamp");
|
||||||
|
model.doCheck(context);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}, const []);
|
||||||
|
|
||||||
|
return makeDefaultPage(context,
|
||||||
|
title: "一键诊断",
|
||||||
|
useBodyContainer: true,
|
||||||
|
content: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
for (final item in const {
|
||||||
|
"rsi_log": "RSI启动器log",
|
||||||
|
"game_log": "游戏运行log",
|
||||||
|
}.entries)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||||
|
child: Button(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(FluentIcons.folder_open),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(item.value),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
_onTapButton(context, item.key, homeState)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (state.isChecking)
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const ProgressRing(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(state.lastScreenInfo)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
else if (state.checkResult == null ||
|
||||||
|
state.checkResult!.isEmpty) ...[
|
||||||
|
const Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text("扫描完毕,没有找到问题!", maxLines: 1),
|
||||||
|
SizedBox(height: 64),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
] else
|
||||||
|
...makeResult(context, state, model),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (state.isFixing)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withAlpha(150),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const ProgressRing(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(state.isFixingString.isNotEmpty
|
||||||
|
? state.isFixingString
|
||||||
|
: "正在处理..."),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 20,
|
||||||
|
right: 20,
|
||||||
|
child: makeRescueBanner(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeRescueBanner(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await showToast(context,
|
||||||
|
"您即将前往由 深空治疗中心(QQ群号:536454632 ) 提供的游戏异常救援服务,主要解决游戏安装失败与频繁闪退,如游戏玩法问题,请勿加群。");
|
||||||
|
launchUrlString(
|
||||||
|
"https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=-M4wEme_bCXbUGT4LFKLH0bAYTFt70Ad&authKey=vHVr0TNgRmKu%2BHwywoJV6EiLa7La2VX74Vkyixr05KA0H9TqB6qWlCdY%2B9jLQ4Ha&noverify=0&group_code=536454632");
|
||||||
|
},
|
||||||
|
child: Tilt(
|
||||||
|
shadowConfig: const ShadowConfig(maxIntensity: .2),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Image.asset("assets/rescue.png", width: 24, height: 24),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Text("需要帮助? 点击加群寻求免费人工支援!"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> makeResult(BuildContext context, HomeGameDoctorState state,
|
||||||
|
HomeGameDoctorUIModel model) {
|
||||||
|
return [
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Text(state.lastScreenInfo, maxLines: 1),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
"注意:本工具检测结果仅供参考,若您不理解以下操作,请提供截图给有经验的玩家!",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ListView.builder(
|
||||||
|
itemCount: state.checkResult!.length,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final item = state.checkResult![index];
|
||||||
|
return makeResultItem(context, item, state, model);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 64),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeResultItem(BuildContext context, MapEntry<String, String> item,
|
||||||
|
HomeGameDoctorState state, HomeGameDoctorUIModel model) {
|
||||||
|
final errorNames = {
|
||||||
|
"unSupport_system":
|
||||||
|
MapEntry("不支持的操作系统,游戏可能无法运行", "请升级您的系统 (${item.value})"),
|
||||||
|
"no_live_path": MapEntry("安装目录缺少LIVE文件夹,可能导致安装失败",
|
||||||
|
"点击修复为您创建 LIVE 文件夹,完成后重试安装。(${item.value})"),
|
||||||
|
"nvme_PhysicalBytes": MapEntry("新型 NVME 设备,与 RSI 启动器暂不兼容,可能导致安装失败",
|
||||||
|
"为注册表项添加 ForcedPhysicalSectorSizeInBytes 值 模拟旧设备。硬盘分区(${item.value})"),
|
||||||
|
"eac_file_miss": const MapEntry("EasyAntiCheat 文件丢失",
|
||||||
|
"未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件"),
|
||||||
|
"eac_not_install": const MapEntry("EasyAntiCheat 未安装 或 未正常退出",
|
||||||
|
"EasyAntiCheat 未安装,请点击修复为您一键安装。(在游戏正常启动并结束前,该问题会一直出现,若您因为其他原因游戏闪退,可忽略此条目)"),
|
||||||
|
"cn_user_name":
|
||||||
|
const MapEntry("中文用户名!", "中文用户名可能会导致游戏启动/安装错误! 点击修复按钮查看修改教程!"),
|
||||||
|
"cn_install_path": MapEntry("中文安装路径!",
|
||||||
|
"中文安装路径!这可能会导致游戏 启动/安装 错误!(${item.value}),请在RSI启动器更换安装路径。"),
|
||||||
|
"low_ram": MapEntry(
|
||||||
|
"物理内存过低", "您至少需要 16GB 的物理内存(Memory)才可运行此游戏。(当前大小:${item.value})"),
|
||||||
|
};
|
||||||
|
bool isCheckedError = errorNames.containsKey(item.key);
|
||||||
|
|
||||||
|
if (isCheckedError) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
errorNames[item.key]?.key ?? "",
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
subtitle: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
"修复建议: ${errorNames[item.key]?.value ?? "暂无解决方法,请截图反馈"}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: Colors.white.withOpacity(.7)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Button(
|
||||||
|
onPressed: (errorNames[item.key]?.value == null || state.isFixing)
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await model.doFix(context, item);
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||||
|
child: Text("修复"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final isSubTitleUrl = item.value.startsWith("https://");
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
item.key,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
subtitle: isSubTitleUrl
|
||||||
|
? null
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
item.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: Colors.white.withOpacity(.7)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: isSubTitleUrl
|
||||||
|
? Button(
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(item.value);
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||||
|
child: Text("查看解决方案"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onTapButton(
|
||||||
|
BuildContext context, String key, HomeUIModelState homeState) async {
|
||||||
|
switch (key) {
|
||||||
|
case "rsi_log":
|
||||||
|
final path = await SCLoggerHelper.getLogFilePath();
|
||||||
|
if (path == null) return;
|
||||||
|
SystemHelper.openDir(path);
|
||||||
|
return;
|
||||||
|
case "game_log":
|
||||||
|
if (homeState.scInstalledPath == "not_install" ||
|
||||||
|
homeState.scInstalledPath == null) {
|
||||||
|
showToast(context, "请在首页选择游戏安装目录。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SystemHelper.openDir("${homeState.scInstalledPath}\\Game.log");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
273
lib/ui/home/game_doctor/game_doctor_ui_model.dart
Normal file
273
lib/ui/home/game_doctor/game_doctor_ui_model.dart
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
part 'game_doctor_ui_model.g.dart';
|
||||||
|
|
||||||
|
part 'game_doctor_ui_model.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class HomeGameDoctorState with _$HomeGameDoctorState {
|
||||||
|
const factory HomeGameDoctorState({
|
||||||
|
@Default(false) bool isChecking,
|
||||||
|
@Default(false) bool isFixing,
|
||||||
|
@Default("") String lastScreenInfo,
|
||||||
|
@Default("") String isFixingString,
|
||||||
|
List<MapEntry<String, String>>? checkResult,
|
||||||
|
}) = _HomeGameDoctorState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class HomeGameDoctorUIModel extends _$HomeGameDoctorUIModel {
|
||||||
|
@override
|
||||||
|
HomeGameDoctorState build() {
|
||||||
|
state = const HomeGameDoctorState();
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> doFix(
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
BuildContext context,
|
||||||
|
MapEntry<String, String> item) async {
|
||||||
|
final checkResult =
|
||||||
|
List<MapEntry<String, String>>.from(state.checkResult ?? []);
|
||||||
|
state = state.copyWith(isFixing: true, isFixingString: "");
|
||||||
|
switch (item.key) {
|
||||||
|
case "unSupport_system":
|
||||||
|
showToast(context, "若您的硬件达标,请尝试安装最新的 Windows 系统。");
|
||||||
|
break;
|
||||||
|
case "no_live_path":
|
||||||
|
try {
|
||||||
|
await Directory(item.value).create(recursive: true);
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context, "创建文件夹成功,请尝试继续下载游戏!");
|
||||||
|
checkResult.remove(item);
|
||||||
|
state = state.copyWith(checkResult: checkResult);
|
||||||
|
} catch (e) {
|
||||||
|
showToast(context, "创建文件夹失败,请尝试手动创建。\n目录:${item.value} \n错误:$e");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "nvme_PhysicalBytes":
|
||||||
|
final r = await SystemHelper.addNvmePatch();
|
||||||
|
if (r == "") {
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context,
|
||||||
|
"修复成功,请尝试重启后继续安装游戏! 若注册表修改操作导致其他软件出现兼容问题,请使用 工具 中的 NVME 注册表清理。");
|
||||||
|
checkResult.remove(item);
|
||||||
|
state = state.copyWith(checkResult: checkResult);
|
||||||
|
} else {
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context, "修复失败,$r");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "eac_file_miss":
|
||||||
|
showToast(
|
||||||
|
context, "未在 LIVE 文件夹找到 EasyAntiCheat 文件 或 文件不完整,请使用 RSI 启动器校验文件");
|
||||||
|
break;
|
||||||
|
case "eac_not_install":
|
||||||
|
final eacJsonPath = "${item.value}\\Settings.json";
|
||||||
|
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||||
|
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||||
|
final eacID = eacJson["productid"];
|
||||||
|
try {
|
||||||
|
var result = await Process.run(
|
||||||
|
"${item.value}\\EasyAntiCheat_EOS_Setup.exe", ["install", eacID]);
|
||||||
|
dPrint("${item.value}\\EasyAntiCheat_EOS_Setup.exe install $eacID");
|
||||||
|
if (result.stderr == "") {
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context, "修复成功,请尝试启动游戏。(若问题无法解决,请使用工具箱的 《重装 EAC》)");
|
||||||
|
checkResult.remove(item);
|
||||||
|
state = state.copyWith(checkResult: checkResult);
|
||||||
|
} else {
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context, "修复失败,${result.stderr}");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!context.mounted) break;
|
||||||
|
showToast(context, "修复失败,$e");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "cn_user_name":
|
||||||
|
showToast(context, "即将跳转,教程来自互联网,请谨慎操作...");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
|
launchUrlString(
|
||||||
|
"https://btfy.eu.org/?q=5L+u5pS5d2luZG93c+eUqOaIt+WQjeS7juS4reaWh+WIsOiLseaWhw==");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showToast(context, "该问题暂不支持自动处理,请提供截图寻求帮助");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = state.copyWith(isFixing: false, isFixingString: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
doCheck(BuildContext context) async {
|
||||||
|
if (state.isChecking) return;
|
||||||
|
state = state.copyWith(isChecking: true, lastScreenInfo: "正在分析...");
|
||||||
|
dPrint("-------- start docker check -----");
|
||||||
|
if (!context.mounted) return;
|
||||||
|
await _statCheck(context);
|
||||||
|
state = state.copyWith(isChecking: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
_statCheck(BuildContext context) async {
|
||||||
|
final homeState = ref.read(homeUIModelProvider);
|
||||||
|
final scInstalledPath = homeState.scInstalledPath!;
|
||||||
|
|
||||||
|
final checkResult = <MapEntry<String, String>>[];
|
||||||
|
// TODO for debug
|
||||||
|
// checkResult?.add(const MapEntry("unSupport_system", "android"));
|
||||||
|
// checkResult?.add(const MapEntry("nvme_PhysicalBytes", "C"));
|
||||||
|
// checkResult?.add(const MapEntry("no_live_path", ""));
|
||||||
|
|
||||||
|
await _checkPreInstall(context, scInstalledPath, checkResult);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
await _checkEAC(context, scInstalledPath, checkResult);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
await _checkGameRunningLog(context, scInstalledPath, checkResult);
|
||||||
|
|
||||||
|
if (checkResult.isEmpty) {
|
||||||
|
const lastScreenInfo = "分析完毕,没有发现问题";
|
||||||
|
state = state.copyWith(checkResult: null, lastScreenInfo: lastScreenInfo);
|
||||||
|
} else {
|
||||||
|
final lastScreenInfo = "分析完毕,发现 ${checkResult.length} 个问题";
|
||||||
|
state = state.copyWith(
|
||||||
|
checkResult: checkResult, lastScreenInfo: lastScreenInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scInstalledPath == "not_install" && (checkResult.isEmpty)) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showToast(context, "扫描完毕,没有发现问题,若仍然安装失败,请尝试使用工具箱中的 RSI启动器管理员模式。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
Future _checkGameRunningLog(BuildContext context, String scInstalledPath,
|
||||||
|
List<MapEntry<String, String>> checkResult) async {
|
||||||
|
if (scInstalledPath == "not_install") return;
|
||||||
|
const lastScreenInfo = "正在检查:Game.log";
|
||||||
|
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||||
|
final logs = await SCLoggerHelper.getGameRunningLogs(scInstalledPath);
|
||||||
|
if (logs == null) return;
|
||||||
|
final info = SCLoggerHelper.getGameRunningLogInfo(logs);
|
||||||
|
if (info != null) {
|
||||||
|
if (info.key != "_") {
|
||||||
|
checkResult.add(MapEntry("游戏异常退出:${info.key}", info.value));
|
||||||
|
} else {
|
||||||
|
checkResult
|
||||||
|
.add(MapEntry("游戏异常退出:未知异常", "info:${info.value},请点击右下角加群反馈。"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
Future _checkEAC(BuildContext context, String scInstalledPath,
|
||||||
|
List<MapEntry<String, String>> checkResult) async {
|
||||||
|
if (scInstalledPath == "not_install") return;
|
||||||
|
const lastScreenInfo = "正在检查:EAC";
|
||||||
|
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||||
|
|
||||||
|
final eacPath = "$scInstalledPath\\EasyAntiCheat";
|
||||||
|
final eacJsonPath = "$eacPath\\Settings.json";
|
||||||
|
if (!await Directory(eacPath).exists() ||
|
||||||
|
!await File(eacJsonPath).exists()) {
|
||||||
|
checkResult.add(const MapEntry("eac_file_miss", ""));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final eacJsonData = await File(eacJsonPath).readAsBytes();
|
||||||
|
final Map eacJson = json.decode(utf8.decode(eacJsonData));
|
||||||
|
final eacID = eacJson["productid"];
|
||||||
|
final eacDeploymentId = eacJson["deploymentid"];
|
||||||
|
if (eacID == null || eacDeploymentId == null) {
|
||||||
|
checkResult.add(const MapEntry("eac_file_miss", ""));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final eacFilePath =
|
||||||
|
"${Platform.environment["appdata"]}\\EasyAntiCheat\\$eacID\\$eacDeploymentId\\anticheatlauncher.log";
|
||||||
|
if (!await File(eacFilePath).exists()) {
|
||||||
|
checkResult.add(MapEntry("eac_not_install", eacPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final cnExp = RegExp(r"[^\x00-\xff]");
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
Future _checkPreInstall(BuildContext context, String scInstalledPath,
|
||||||
|
List<MapEntry<String, String>> checkResult) async {
|
||||||
|
const lastScreenInfo = "正在检查:运行环境";
|
||||||
|
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||||
|
|
||||||
|
if (!(Platform.operatingSystemVersion.contains("Windows 10") ||
|
||||||
|
Platform.operatingSystemVersion.contains("Windows 11"))) {
|
||||||
|
checkResult
|
||||||
|
.add(MapEntry("unSupport_system", Platform.operatingSystemVersion));
|
||||||
|
final lastScreenInfo = "不支持的操作系统:${Platform.operatingSystemVersion}";
|
||||||
|
state = state.copyWith(lastScreenInfo: lastScreenInfo);
|
||||||
|
await showToast(context, lastScreenInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnExp.hasMatch(await SCLoggerHelper.getLogFilePath() ?? "")) {
|
||||||
|
checkResult.add(const MapEntry("cn_user_name", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 RAM
|
||||||
|
final ramSize = await SystemHelper.getSystemMemorySizeGB();
|
||||||
|
if (ramSize < 16) {
|
||||||
|
checkResult.add(MapEntry("low_ram", "$ramSize"));
|
||||||
|
}
|
||||||
|
state = state.copyWith(lastScreenInfo: "正在检查:安装信息");
|
||||||
|
// 检查安装分区
|
||||||
|
try {
|
||||||
|
final listData = await SCLoggerHelper.getGameInstallPath(
|
||||||
|
await SCLoggerHelper.getLauncherLogList() ?? []);
|
||||||
|
final p = [];
|
||||||
|
final checkedPath = [];
|
||||||
|
for (var installPath in listData) {
|
||||||
|
if (!checkedPath.contains(installPath)) {
|
||||||
|
if (cnExp.hasMatch(installPath)) {
|
||||||
|
checkResult.add(MapEntry("cn_install_path", installPath));
|
||||||
|
}
|
||||||
|
if (scInstalledPath == "not_install") {
|
||||||
|
checkedPath.add(installPath);
|
||||||
|
if (!await Directory(installPath).exists()) {
|
||||||
|
checkResult.add(MapEntry("no_live_path", installPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final tp = installPath.split(":")[0];
|
||||||
|
if (!p.contains(tp)) {
|
||||||
|
p.add(tp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// call check
|
||||||
|
for (var element in p) {
|
||||||
|
var result = await Process.run('powershell', [
|
||||||
|
"(fsutil fsinfo sectorinfo $element: | Select-String 'PhysicalBytesPerSectorForPerformance').ToString().Split(':')[1].Trim()"
|
||||||
|
]);
|
||||||
|
dPrint(
|
||||||
|
"fsutil info sector info: ->>> ${result.stdout.toString().trim()}");
|
||||||
|
if (result.stderr == "") {
|
||||||
|
final rs = result.stdout.toString().trim();
|
||||||
|
final physicalBytesPerSectorForPerformance = (int.tryParse(rs) ?? 0);
|
||||||
|
if (physicalBytesPerSectorForPerformance > 4096) {
|
||||||
|
checkResult.add(MapEntry("nvme_PhysicalBytes", element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
dPrint(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
242
lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart
Normal file
242
lib/ui/home/game_doctor/game_doctor_ui_model.freezed.dart
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'game_doctor_ui_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$HomeGameDoctorState {
|
||||||
|
bool get isChecking => throw _privateConstructorUsedError;
|
||||||
|
bool get isFixing => throw _privateConstructorUsedError;
|
||||||
|
String get lastScreenInfo => throw _privateConstructorUsedError;
|
||||||
|
String get isFixingString => throw _privateConstructorUsedError;
|
||||||
|
List<MapEntry<String, String>>? get checkResult =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$HomeGameDoctorStateCopyWith<HomeGameDoctorState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $HomeGameDoctorStateCopyWith<$Res> {
|
||||||
|
factory $HomeGameDoctorStateCopyWith(
|
||||||
|
HomeGameDoctorState value, $Res Function(HomeGameDoctorState) then) =
|
||||||
|
_$HomeGameDoctorStateCopyWithImpl<$Res, HomeGameDoctorState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{bool isChecking,
|
||||||
|
bool isFixing,
|
||||||
|
String lastScreenInfo,
|
||||||
|
String isFixingString,
|
||||||
|
List<MapEntry<String, String>>? checkResult});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$HomeGameDoctorStateCopyWithImpl<$Res, $Val extends HomeGameDoctorState>
|
||||||
|
implements $HomeGameDoctorStateCopyWith<$Res> {
|
||||||
|
_$HomeGameDoctorStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? isChecking = null,
|
||||||
|
Object? isFixing = null,
|
||||||
|
Object? lastScreenInfo = null,
|
||||||
|
Object? isFixingString = null,
|
||||||
|
Object? checkResult = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
isChecking: null == isChecking
|
||||||
|
? _value.isChecking
|
||||||
|
: isChecking // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isFixing: null == isFixing
|
||||||
|
? _value.isFixing
|
||||||
|
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastScreenInfo: null == lastScreenInfo
|
||||||
|
? _value.lastScreenInfo
|
||||||
|
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
isFixingString: null == isFixingString
|
||||||
|
? _value.isFixingString
|
||||||
|
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
checkResult: freezed == checkResult
|
||||||
|
? _value.checkResult
|
||||||
|
: checkResult // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<MapEntry<String, String>>?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$HomeGameDoctorStateImplCopyWith<$Res>
|
||||||
|
implements $HomeGameDoctorStateCopyWith<$Res> {
|
||||||
|
factory _$$HomeGameDoctorStateImplCopyWith(_$HomeGameDoctorStateImpl value,
|
||||||
|
$Res Function(_$HomeGameDoctorStateImpl) then) =
|
||||||
|
__$$HomeGameDoctorStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{bool isChecking,
|
||||||
|
bool isFixing,
|
||||||
|
String lastScreenInfo,
|
||||||
|
String isFixingString,
|
||||||
|
List<MapEntry<String, String>>? checkResult});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$HomeGameDoctorStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$HomeGameDoctorStateCopyWithImpl<$Res, _$HomeGameDoctorStateImpl>
|
||||||
|
implements _$$HomeGameDoctorStateImplCopyWith<$Res> {
|
||||||
|
__$$HomeGameDoctorStateImplCopyWithImpl(_$HomeGameDoctorStateImpl _value,
|
||||||
|
$Res Function(_$HomeGameDoctorStateImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? isChecking = null,
|
||||||
|
Object? isFixing = null,
|
||||||
|
Object? lastScreenInfo = null,
|
||||||
|
Object? isFixingString = null,
|
||||||
|
Object? checkResult = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$HomeGameDoctorStateImpl(
|
||||||
|
isChecking: null == isChecking
|
||||||
|
? _value.isChecking
|
||||||
|
: isChecking // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isFixing: null == isFixing
|
||||||
|
? _value.isFixing
|
||||||
|
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastScreenInfo: null == lastScreenInfo
|
||||||
|
? _value.lastScreenInfo
|
||||||
|
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
isFixingString: null == isFixingString
|
||||||
|
? _value.isFixingString
|
||||||
|
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
checkResult: freezed == checkResult
|
||||||
|
? _value._checkResult
|
||||||
|
: checkResult // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<MapEntry<String, String>>?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$HomeGameDoctorStateImpl implements _HomeGameDoctorState {
|
||||||
|
const _$HomeGameDoctorStateImpl(
|
||||||
|
{this.isChecking = false,
|
||||||
|
this.isFixing = false,
|
||||||
|
this.lastScreenInfo = "",
|
||||||
|
this.isFixingString = "",
|
||||||
|
final List<MapEntry<String, String>>? checkResult})
|
||||||
|
: _checkResult = checkResult;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isChecking;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFixing;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String lastScreenInfo;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String isFixingString;
|
||||||
|
final List<MapEntry<String, String>>? _checkResult;
|
||||||
|
@override
|
||||||
|
List<MapEntry<String, String>>? get checkResult {
|
||||||
|
final value = _checkResult;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_checkResult is EqualUnmodifiableListView) return _checkResult;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'HomeGameDoctorState(isChecking: $isChecking, isFixing: $isFixing, lastScreenInfo: $lastScreenInfo, isFixingString: $isFixingString, checkResult: $checkResult)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$HomeGameDoctorStateImpl &&
|
||||||
|
(identical(other.isChecking, isChecking) ||
|
||||||
|
other.isChecking == isChecking) &&
|
||||||
|
(identical(other.isFixing, isFixing) ||
|
||||||
|
other.isFixing == isFixing) &&
|
||||||
|
(identical(other.lastScreenInfo, lastScreenInfo) ||
|
||||||
|
other.lastScreenInfo == lastScreenInfo) &&
|
||||||
|
(identical(other.isFixingString, isFixingString) ||
|
||||||
|
other.isFixingString == isFixingString) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._checkResult, _checkResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
isChecking,
|
||||||
|
isFixing,
|
||||||
|
lastScreenInfo,
|
||||||
|
isFixingString,
|
||||||
|
const DeepCollectionEquality().hash(_checkResult));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$HomeGameDoctorStateImplCopyWith<_$HomeGameDoctorStateImpl> get copyWith =>
|
||||||
|
__$$HomeGameDoctorStateImplCopyWithImpl<_$HomeGameDoctorStateImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _HomeGameDoctorState implements HomeGameDoctorState {
|
||||||
|
const factory _HomeGameDoctorState(
|
||||||
|
{final bool isChecking,
|
||||||
|
final bool isFixing,
|
||||||
|
final String lastScreenInfo,
|
||||||
|
final String isFixingString,
|
||||||
|
final List<MapEntry<String, String>>? checkResult}) =
|
||||||
|
_$HomeGameDoctorStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isChecking;
|
||||||
|
@override
|
||||||
|
bool get isFixing;
|
||||||
|
@override
|
||||||
|
String get lastScreenInfo;
|
||||||
|
@override
|
||||||
|
String get isFixingString;
|
||||||
|
@override
|
||||||
|
List<MapEntry<String, String>>? get checkResult;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$HomeGameDoctorStateImplCopyWith<_$HomeGameDoctorStateImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
27
lib/ui/home/game_doctor/game_doctor_ui_model.g.dart
Normal file
27
lib/ui/home/game_doctor/game_doctor_ui_model.g.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'game_doctor_ui_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$homeGameDoctorUIModelHash() =>
|
||||||
|
r'59cbd6f866bacbc24eb0c0eb0ad88fe7f2dac4a7';
|
||||||
|
|
||||||
|
/// See also [HomeGameDoctorUIModel].
|
||||||
|
@ProviderFor(HomeGameDoctorUIModel)
|
||||||
|
final homeGameDoctorUIModelProvider = AutoDisposeNotifierProvider<
|
||||||
|
HomeGameDoctorUIModel, HomeGameDoctorState>.internal(
|
||||||
|
HomeGameDoctorUIModel.new,
|
||||||
|
name: r'homeGameDoctorUIModelProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$homeGameDoctorUIModelHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$HomeGameDoctorUIModel = AutoDisposeNotifier<HomeGameDoctorState>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -4,6 +4,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
@ -427,7 +428,7 @@ class HomeUI extends HookConsumerWidget {
|
|||||||
Widget makeIndexActionLists(
|
Widget makeIndexActionLists(
|
||||||
BuildContext context, HomeUIModel model, HomeUIModelState homeState) {
|
BuildContext context, HomeUIModel model, HomeUIModelState homeState) {
|
||||||
final items = [
|
final items = [
|
||||||
_HomeItemData("auto_check", "一键诊断", "一键诊断星际公民常见问题",
|
_HomeItemData("game_doctor", "一键诊断", "一键诊断星际公民常见问题",
|
||||||
FluentIcons.auto_deploy_settings),
|
FluentIcons.auto_deploy_settings),
|
||||||
_HomeItemData(
|
_HomeItemData(
|
||||||
"localization", "汉化管理", "快捷安装汉化资源", FluentIcons.locale_language),
|
"localization", "汉化管理", "快捷安装汉化资源", FluentIcons.locale_language),
|
||||||
@ -445,7 +446,7 @@ class HomeUI extends HookConsumerWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = items.elementAt(index);
|
final item = items.elementAt(index);
|
||||||
return HoverButton(
|
return HoverButton(
|
||||||
onPressed: () => model.onMenuTap(item.key),
|
onPressed: () => _onMenuTap(context, item.key),
|
||||||
builder: (BuildContext context, Set<ButtonStates> states) {
|
builder: (BuildContext context, Set<ButtonStates> states) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 300,
|
width: 300,
|
||||||
@ -746,6 +747,10 @@ class HomeUI extends HookConsumerWidget {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context, builder: (context) => const HomeCountdownDialogUI());
|
context: context, builder: (context) => const HomeCountdownDialogUI());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onMenuTap(BuildContext context, String key) {
|
||||||
|
context.push("/index/$key");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomeItemData {
|
class _HomeItemData {
|
||||||
|
@ -121,8 +121,6 @@ class HomeUIModel extends _$HomeUIModel {
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuTap(String key) {}
|
|
||||||
|
|
||||||
// ignore: avoid_build_context_in_providers
|
// ignore: avoid_build_context_in_providers
|
||||||
Future<void> goWebView(BuildContext context, String title, String url,
|
Future<void> goWebView(BuildContext context, String title, String url,
|
||||||
{bool useLocalization = false,
|
{bool useLocalization = false,
|
||||||
|
@ -6,7 +6,7 @@ part of 'home_ui_model.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$homeUIModelHash() => r'3094d9ab828a578670e11f3eaffa57bdb95a004b';
|
String _$homeUIModelHash() => r'4ef5c2bdf2d9a506b2e139d321c52c97cf4bc30d';
|
||||||
|
|
||||||
/// See also [HomeUIModel].
|
/// See also [HomeUIModel].
|
||||||
@ProviderFor(HomeUIModel)
|
@ProviderFor(HomeUIModel)
|
||||||
|
@ -32,7 +32,8 @@ Widget makeDefaultPage(BuildContext context,
|
|||||||
List<Widget>? actions,
|
List<Widget>? actions,
|
||||||
Widget? content,
|
Widget? content,
|
||||||
bool automaticallyImplyLeading = true,
|
bool automaticallyImplyLeading = true,
|
||||||
String title = ""}) {
|
String title = "",
|
||||||
|
bool useBodyContainer = false}) {
|
||||||
return NavigationView(
|
return NavigationView(
|
||||||
appBar: NavigationAppBar(
|
appBar: NavigationAppBar(
|
||||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||||
@ -54,7 +55,15 @@ Widget makeDefaultPage(BuildContext context,
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [...?actions, const WindowButtons()],
|
children: [...?actions, const WindowButtons()],
|
||||||
)),
|
)),
|
||||||
content: content,
|
content: useBodyContainer
|
||||||
|
? Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).scaffoldBackgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(9),
|
||||||
|
),
|
||||||
|
child: content,
|
||||||
|
)
|
||||||
|
: content,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,18 +137,19 @@ CustomTransitionPage<T> myPageBuilder<T>(
|
|||||||
BuildContext context, GoRouterState state, Widget child) {
|
BuildContext context, GoRouterState state, Widget child) {
|
||||||
return CustomTransitionPage(
|
return CustomTransitionPage(
|
||||||
child: child,
|
child: child,
|
||||||
|
transitionDuration: const Duration(milliseconds: 150),
|
||||||
|
reverseTransitionDuration: const Duration(milliseconds: 150),
|
||||||
transitionsBuilder: (BuildContext context, Animation<double> animation,
|
transitionsBuilder: (BuildContext context, Animation<double> animation,
|
||||||
Animation<double> secondaryAnimation, Widget child) {
|
Animation<double> secondaryAnimation, Widget child) {
|
||||||
return Semantics(
|
return SlideTransition(
|
||||||
scopesRoute: true,
|
position: Tween<Offset>(
|
||||||
explicitChildNodes: true,
|
begin: const Offset(0.0, 1.0),
|
||||||
child: EntrancePageTransition(
|
end: const Offset(0.0, 0.0),
|
||||||
animation: CurvedAnimation(
|
).animate(CurvedAnimation(
|
||||||
parent: animation,
|
parent: animation,
|
||||||
curve: FluentTheme.of(context).animationCurve,
|
curve: Curves.easeInOut,
|
||||||
),
|
)),
|
||||||
child: child,
|
child: child,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user