mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-06-28 17:14:48 +08:00
feat:riverpod 迁移
This commit is contained in:
@ -1,268 +0,0 @@
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:starcitizen_doctor/base/ui.dart';
|
||||
import 'package:starcitizen_doctor/data/game_performance_data.dart';
|
||||
|
||||
import 'performance_ui_model.dart';
|
||||
|
||||
class PerformanceUI extends BaseUI<PerformanceUIModel> {
|
||||
@override
|
||||
Widget? buildBody(BuildContext context, PerformanceUIModel model) {
|
||||
var content = makeLoading(context);
|
||||
|
||||
if (model.performanceMap != null) {
|
||||
content = Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, left: 12, right: 12),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
if (model.showGraphicsPerformanceTip)
|
||||
InfoBar(
|
||||
title: const Text("图形优化提示"),
|
||||
content: const Text(
|
||||
"该功能对优化显卡瓶颈有很大帮助,但对 CPU 瓶颈可能起反效果,如果您显卡性能强劲,可以尝试使用更好的画质来获得更高的显卡利用率。",
|
||||
),
|
||||
onClose: () => model.closeTip(),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"当前状态:${model.enabled ? "已应用" : "未应用"}",
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
const Text(
|
||||
"预设:",
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
for (final item in const {
|
||||
"low": "低",
|
||||
"medium": "中",
|
||||
"high": "高",
|
||||
"ultra": "超级"
|
||||
}.entries)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
child: Button(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 2, bottom: 2, left: 4, right: 4),
|
||||
child: Text(item.value),
|
||||
),
|
||||
onPressed: () =>
|
||||
model.onChangePreProfile(item.key)),
|
||||
),
|
||||
const Text("(预设只修改图形设置)"),
|
||||
const Spacer(),
|
||||
Button(
|
||||
onPressed: () => model.refresh(),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(6),
|
||||
child: Icon(FluentIcons.refresh),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
child: const Text(
|
||||
" 恢复默认 ",
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
onPressed: () => model.clean()),
|
||||
const SizedBox(width: 24),
|
||||
Button(
|
||||
child: const Text(
|
||||
"应用",
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
onPressed: () => model.applyProfile(false)),
|
||||
const SizedBox(width: 6),
|
||||
Button(
|
||||
child: const Text(
|
||||
"应用并清理着色器(推荐)",
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
onPressed: () => model.applyProfile(true)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: MasonryGridView.count(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 1,
|
||||
crossAxisSpacing: 1,
|
||||
itemCount: model.performanceMap!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return makeItemGroup(
|
||||
model.performanceMap!.entries.elementAt(index));
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (model.workingString.isNotEmpty)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(150),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ProgressRing(),
|
||||
const SizedBox(height: 12),
|
||||
Text(model.workingString),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return makeDefaultPage(context, model, content: content);
|
||||
}
|
||||
|
||||
Widget makeItemGroup(MapEntry<String?, List<GamePerformanceData>> group) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: FluentTheme.of(context).cardColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${group.key}",
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
color: FluentTheme.of(context).cardColor.withOpacity(.2),
|
||||
height: 1),
|
||||
const SizedBox(height: 6),
|
||||
for (final item in group.value) makeItem(item)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeItem(GamePerformanceData item) {
|
||||
final model = ref.watch(provider);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${item.name}",
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (item.type == "int")
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 72,
|
||||
child: TextFormBox(
|
||||
key: UniqueKey(),
|
||||
initialValue: "${item.value}",
|
||||
onFieldSubmitted: (str) {
|
||||
dPrint(str);
|
||||
if (str.isEmpty) return;
|
||||
final v = int.tryParse(str);
|
||||
if (v != null &&
|
||||
v < (item.max ?? 0) &&
|
||||
v >= (item.min ?? 0)) {
|
||||
item.value = v;
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
onTapOutside: (e) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 32),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: Slider(
|
||||
value: item.value?.toDouble() ?? 0,
|
||||
min: item.min?.toDouble() ?? 0,
|
||||
max: item.max?.toDouble() ?? 0,
|
||||
onChanged: (double value) {
|
||||
item.value = value.toInt();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
else if (item.type == "bool")
|
||||
Column(
|
||||
children: [
|
||||
ToggleSwitch(
|
||||
checked: item.value == 1,
|
||||
onChanged: (bool value) {
|
||||
item.value = value ? 1 : 0;
|
||||
setState(() {});
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
else if (item.type == "customize")
|
||||
TextFormBox(
|
||||
maxLines: 10,
|
||||
placeholder:
|
||||
"您可以在这里输入未收录进盒子的自定义参数。配置示例:\n\nr_displayinfo=0\nr_VSync=0",
|
||||
controller: model.customizeCtrl,
|
||||
),
|
||||
if (item.info != null && item.info!.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
"${item.info}",
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: Colors.white.withOpacity(.6)),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
if (item.type != "customize")
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"${item.key} 最小值: ${item.min} / 最大值: ${item.max}",
|
||||
style: TextStyle(color: Colors.white.withOpacity(.6)),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
color: FluentTheme.of(context).cardColor.withOpacity(.1),
|
||||
height: 1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String getUITitle(BuildContext context, PerformanceUIModel model) =>
|
||||
"性能优化 ${model.scPath}";
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/data/game_performance_data.dart';
|
||||
|
||||
class PerformanceUIModel extends BaseUIModel {
|
||||
String scPath;
|
||||
|
||||
PerformanceUIModel(this.scPath);
|
||||
|
||||
TextEditingController customizeCtrl = TextEditingController();
|
||||
|
||||
Map<String?, List<GamePerformanceData>>? performanceMap;
|
||||
|
||||
List<String> inAppKeys = [];
|
||||
|
||||
String workingString = "";
|
||||
|
||||
late final confFile = File("$scPath\\USER.cfg");
|
||||
|
||||
bool enabled = false;
|
||||
|
||||
bool showGraphicsPerformanceTip = false;
|
||||
static const _graphicsPerformanceTipVersion = 1;
|
||||
|
||||
@override
|
||||
Future loadData() async {
|
||||
customizeCtrl.clear();
|
||||
inAppKeys.clear();
|
||||
final String jsonString =
|
||||
await rootBundle.loadString('assets/performance.json');
|
||||
final list = json.decode(jsonString);
|
||||
|
||||
if (list is List) {
|
||||
performanceMap = {};
|
||||
for (var element in list) {
|
||||
final item = GamePerformanceData.fromJson(element);
|
||||
if (item.key != "customize") {
|
||||
inAppKeys.add(item.key ?? "");
|
||||
}
|
||||
performanceMap?[item.group] ??= [];
|
||||
performanceMap?[item.group]?.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (await confFile.exists()) {
|
||||
await _readConf();
|
||||
} else {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
final box = await Hive.openBox("app_conf");
|
||||
final v = box.get("close_graphics_performance_tip", defaultValue: -1);
|
||||
showGraphicsPerformanceTip = v != _graphicsPerformanceTipVersion;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
onChangePreProfile(String key) {
|
||||
switch (key) {
|
||||
case "low":
|
||||
performanceMap?.forEach((key, v) {
|
||||
if (key?.contains("图形") ?? false) {
|
||||
for (var element in v) {
|
||||
element.value = element.min;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "medium":
|
||||
performanceMap?.forEach((key, v) {
|
||||
if (key?.contains("图形") ?? false) {
|
||||
for (var element in v) {
|
||||
element.value = ((element.max ?? 0) ~/ 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "high":
|
||||
performanceMap?.forEach((key, v) {
|
||||
if (key?.contains("图形") ?? false) {
|
||||
for (var element in v) {
|
||||
element.value = ((element.max ?? 0) / 1.5).ceil();
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "ultra":
|
||||
performanceMap?.forEach((key, v) {
|
||||
if (key?.contains("图形") ?? false) {
|
||||
for (var element in v) {
|
||||
element.value = element.max;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
applyProfile(bool cleanShader) async {
|
||||
if (performanceMap == null) return;
|
||||
AnalyticsApi.touch("performance_apply");
|
||||
workingString = "生成配置文件";
|
||||
notifyListeners();
|
||||
String conf = "";
|
||||
for (var v in performanceMap!.entries) {
|
||||
for (var c in v.value) {
|
||||
if (c.key != "customize") {
|
||||
conf = "$conf${c.key}=${c.value}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (customizeCtrl.text.trim().isNotEmpty) {
|
||||
final lines = customizeCtrl.text.split("\n");
|
||||
for (var value in lines) {
|
||||
final sp = value.split("=");
|
||||
// 忽略无效的配置文件
|
||||
if (sp.length == 2) {
|
||||
conf = "$conf${sp[0].trim()}=${sp[1].trim()}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
workingString = "写出配置文件";
|
||||
notifyListeners();
|
||||
if (await confFile.exists()) {
|
||||
await confFile.delete();
|
||||
}
|
||||
await confFile.create();
|
||||
await confFile.writeAsString(conf);
|
||||
if (cleanShader) {
|
||||
workingString = "清理着色器";
|
||||
notifyListeners();
|
||||
await _cleanShaderCache();
|
||||
}
|
||||
workingString = "完成...";
|
||||
notifyListeners();
|
||||
await await Future.delayed(const Duration(milliseconds: 300));
|
||||
await reloadData();
|
||||
workingString = "";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _cleanShaderCache() async {
|
||||
final gameShaderCachePath = await SCLoggerHelper.getShaderCachePath();
|
||||
final l =
|
||||
await Directory(gameShaderCachePath!).list(recursive: false).toList();
|
||||
for (var value in l) {
|
||||
if (value is Directory) {
|
||||
if (!value.absolute.path.contains("Crashes")) {
|
||||
await value.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
showToast(context!, "清理着色器后首次进入游戏可能会出现卡顿,请耐心等待游戏初始化完毕。");
|
||||
}
|
||||
|
||||
_readConf() async {
|
||||
if (performanceMap == null) return;
|
||||
enabled = true;
|
||||
final confString = await confFile.readAsString();
|
||||
for (var value in confString.split("\n")) {
|
||||
final kv = value.split("=");
|
||||
for (var m in performanceMap!.entries) {
|
||||
for (var value in m.value) {
|
||||
if (value.key == kv[0].trim()) {
|
||||
final v = int.tryParse(kv[1].trim());
|
||||
if (v != null) value.value = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kv.length == 2 && !inAppKeys.contains(kv[0].trim())) {
|
||||
customizeCtrl.text =
|
||||
"${customizeCtrl.text}${kv[0].trim()}=${kv[1].trim()}\n";
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
clean() async {
|
||||
workingString = "删除配置文件...";
|
||||
notifyListeners();
|
||||
if (await confFile.exists()) {
|
||||
await confFile.delete(recursive: true);
|
||||
}
|
||||
workingString = "清理着色器";
|
||||
notifyListeners();
|
||||
await _cleanShaderCache();
|
||||
workingString = "完成...";
|
||||
await await Future.delayed(const Duration(milliseconds: 300));
|
||||
await reloadData();
|
||||
workingString = "";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
refresh() async {
|
||||
await reloadData();
|
||||
}
|
||||
|
||||
closeTip() async {
|
||||
final box = await Hive.openBox("app_conf");
|
||||
await box.put(
|
||||
"close_graphics_performance_tip", _graphicsPerformanceTipVersion);
|
||||
loadData();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user