feat: 42kit Nav

feat: Animation Optimization
This commit is contained in:
2025-05-04 14:07:56 +08:00
parent a2de310d84
commit 03c941c970
23 changed files with 5618 additions and 493 deletions

View File

@ -30,8 +30,7 @@ class AboutUI extends HookConsumerWidget {
);
}
Widget _makeAbout(BuildContext context, WidgetRef ref,
ValueNotifier<bool> isTipTextCn, PageController pageCtrl) {
Widget _makeAbout(BuildContext context, WidgetRef ref, ValueNotifier<bool> isTipTextCn, PageController pageCtrl) {
return Stack(
children: [
Center(
@ -42,9 +41,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(height: 32),
Image.asset("assets/app_logo.png", width: 128, height: 128),
const SizedBox(height: 6),
Text(
S.current.app_index_version_info(
ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev"),
Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev"),
style: const TextStyle(fontSize: 18)),
const SizedBox(height: 12),
Button(
@ -56,18 +53,15 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(height: 32),
Container(
margin: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: FluentTheme.of(context).cardColor,
borderRadius: BorderRadius.circular(12)),
decoration:
BoxDecoration(color: FluentTheme.of(context).cardColor, borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Text(
S.current.about_app_description,
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: .9)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .9)),
),
],
),
@ -86,9 +80,7 @@ class AboutUI extends HookConsumerWidget {
child: Container(
width: MediaQuery.of(context).size.width * .35,
decoration: BoxDecoration(
color: FluentTheme.of(context)
.cardColor
.withValues(alpha: .06),
color: FluentTheme.of(context).cardColor.withValues(alpha: .06),
borderRadius: BorderRadius.circular(12)),
child: IconButton(
icon: Padding(
@ -96,9 +88,7 @@ class AboutUI extends HookConsumerWidget {
child: Text(
isTipTextCn.value ? tipTextCN : tipTextEN,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
color: Colors.white.withValues(alpha: .9)),
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .9)),
),
),
onPressed: () {
@ -126,8 +116,7 @@ class AboutUI extends HookConsumerWidget {
);
}
Widget _makeDonate(
BuildContext context, WidgetRef ref, PageController pageCtrl) {
Widget _makeDonate(BuildContext context, WidgetRef ref, PageController pageCtrl) {
final donationTypeNotifier = useState('alipay');
final bubbleMessages = [
S.current.support_dev_thanks_message,
@ -171,8 +160,7 @@ class AboutUI extends HookConsumerWidget {
for (var i = 0; i < bubbleMessages.length; i++)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: SelectionArea(
child: ChatBubble(message: bubbleMessages[i])),
child: SelectionArea(child: ChatBubble(message: bubbleMessages[i])),
),
],
),
@ -261,14 +249,10 @@ class AboutUI extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Button(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith((states) =>
isSelected
? ButtonThemeData.buttonColor(context, states)
.withAlpha((255.0 * 0.08).round())
: ButtonThemeData.buttonColor(context, states)
.withAlpha((255.0 * 0.005).round())),
padding: WidgetStateProperty.all(
EdgeInsets.symmetric(horizontal: 16, vertical: 8)),
backgroundColor: WidgetStateProperty.resolveWith((states) => isSelected
? ButtonThemeData.buttonColor(context, states).withAlpha((255.0 * 0.08).round())
: ButtonThemeData.buttonColor(context, states).withAlpha((255.0 * 0.005).round())),
padding: WidgetStateProperty.all(EdgeInsets.symmetric(horizontal: 16, vertical: 8)),
),
onPressed: onTap,
child: Column(
@ -328,16 +312,13 @@ class AboutUI extends HookConsumerWidget {
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: FluentTheme.of(context)
.cardColor
.withAlpha((255 * .1).round()),
color: FluentTheme.of(context).cardColor.withAlpha((255 * .1).round()),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(S.current.support_dev_in_game_id,
style: TextStyle(fontSize: 16)),
Text(S.current.support_dev_in_game_id, style: TextStyle(fontSize: 16)),
const SizedBox(width: 12),
Button(
onPressed: () {
@ -386,8 +367,7 @@ class AboutUI extends HookConsumerWidget {
return Column(
key: ValueKey(type),
children: [
Text(title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Container(
width: 200,
@ -413,19 +393,13 @@ class AboutUI extends HookConsumerWidget {
icon: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
pageIndex == 0
? FluentIcons.chevron_up
: FluentIcons.chevron_down,
size: 12),
Icon(pageIndex == 0 ? FluentIcons.chevron_up : FluentIcons.chevron_down, size: 12),
SizedBox(width: 8),
Text(pageIndex == 0
? S.current.support_dev_back_button
: S.current.support_dev_scroll_hint),
Text(pageIndex == 0 ? S.current.support_dev_back_button : S.current.support_dev_scroll_hint),
],
),
onPressed: () => pageCtrl.animateToPage(pageIndex,
duration: const Duration(milliseconds: 300), curve: Curves.ease),
onPressed: () =>
pageCtrl.animateToPage(pageIndex, duration: const Duration(milliseconds: 300), curve: Curves.ease),
);
}
@ -440,8 +414,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(width: 8),
Text(
S.current.about_action_btn_faq,
style: TextStyle(
fontSize: 14, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
),
@ -457,8 +430,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(width: 8),
Text(
S.current.about_online_feedback,
style: TextStyle(
fontSize: 14, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
),
@ -474,8 +446,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(width: 8),
Text(
S.current.about_action_qq_group,
style: TextStyle(
fontSize: 14, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
),
@ -492,8 +463,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(width: 8),
Text(
S.current.about_action_email,
style: TextStyle(
fontSize: 14, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
),
@ -509,8 +479,7 @@ class AboutUI extends HookConsumerWidget {
const SizedBox(width: 8),
Text(
S.current.about_action_open_source,
style: TextStyle(
fontSize: 14, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
),
@ -528,6 +497,7 @@ class AboutUI extends HookConsumerWidget {
static String get tipTextCN => S.current.about_disclaimer;
Widget makeAnalyticsWidget(BuildContext context) {
var buildIndex = 0;
return LoadingWidget(
onLoadData: AnalyticsApi.getAnalyticsData,
autoRefreshDuration: const Duration(seconds: 60),
@ -547,20 +517,18 @@ class AboutUI extends HookConsumerWidget {
"performance_apply",
"p4k_download",
].contains(item["Type"]))
makeAnalyticsItem(
context: context,
name: item["Type"] as String,
value: item["Count"] as int)
GridItemAnimator(
index: buildIndex++,
child: makeAnalyticsItem(
context: context, name: item["Type"] as String, value: item["Count"] as int),
)
],
);
},
);
}
Widget makeAnalyticsItem(
{required BuildContext context,
required String name,
required int value}) {
Widget makeAnalyticsItem({required BuildContext context, required String name, required int value}) {
final names = {
"launch": S.current.about_analytics_launch,
"gameLaunch": S.current.about_analytics_launch_game,
@ -573,14 +541,12 @@ class AboutUI extends HookConsumerWidget {
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.only(left: 18, right: 18),
decoration: BoxDecoration(
color: FluentTheme.of(context).cardColor.withValues(alpha: .06),
borderRadius: BorderRadius.circular(12)),
color: FluentTheme.of(context).cardColor.withValues(alpha: .06), borderRadius: BorderRadius.circular(12)),
child: Column(
children: [
Text(
names[name] ?? name,
style: TextStyle(
fontSize: 13, color: Colors.white.withValues(alpha: .6)),
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 4),
Row(
@ -606,8 +572,7 @@ class AboutUI extends HookConsumerWidget {
launchUrlString("ms-windows-store://pdp/?productid=9NF3SWFWNKL1");
return;
} else {
final hasUpdate =
await ref.read(appGlobalModelProvider.notifier).checkUpdate(context);
final hasUpdate = await ref.read(appGlobalModelProvider.notifier).checkUpdate(context);
if (!hasUpdate) {
if (!context.mounted) return;
showToast(context, S.current.about_info_latest_version);
@ -626,8 +591,7 @@ class ChatBubble extends StatelessWidget {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color:
FluentTheme.of(context).accentColor.withAlpha((255.0 * .2).round()),
color: FluentTheme.of(context).accentColor.withAlpha((255.0 * .2).round()),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4),
topRight: Radius.circular(18),
@ -645,8 +609,7 @@ class ChatBubble extends StatelessWidget {
class DonationQrCodeData {
static const alipay = "https://qr.alipay.com/tsx16308c4uai0ticmz4j96";
static const wechat =
"wxp://f2f0J40rTCX7Vt79yooWNbiqH3U6UmwGJkqjcAYnrv9OZVzKyS5_W6trp8mo3KP-CTQ5";
static const wechat = "wxp://f2f0J40rTCX7Vt79yooWNbiqH3U6UmwGJkqjcAYnrv9OZVzKyS5_W6trp8mo3KP-CTQ5";
static const qq =
"https://i.qianbao.qq.com/wallet/sqrcode.htm?m=tenpay&f=wallet&a=1&u=3334969096&n=xkeyC&ac=CAEQiK6etgwY8ZuKvgYyGOa1geWKqOaRiuS9jee7j-iQpeaUtuasvjgBQiAzY2Y4NWY3MDI1MWUxYWEwMGYyN2Q0OTM4Y2U2ODFlMw%3D%3D_xxx_sign";
}

View File

@ -28,8 +28,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
onSwitchFile() async {
final sb = await showDialog(
context: context,
builder: (BuildContext context) =>
const LocalizationFromFileDialogUI(isInAdvancedMode: true),
builder: (BuildContext context) => const LocalizationFromFileDialogUI(isInAdvancedMode: true),
);
if (sb is (StringBuffer, bool)) {
model.setCustomizeGlobalIni(sb.$1.toString());
@ -42,8 +41,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
}, const []);
return makeDefaultPage(
title: S.current.home_localization_advanced_title(
homeUIState.scInstalledPath ?? "-"),
title: S.current.home_localization_advanced_title(homeUIState.scInstalledPath ?? "-"),
context,
content: state.workingText.isNotEmpty
? Center(
@ -71,15 +69,13 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
children: [
Text(
S.current.home_localization_advanced_msg_version(
state.apiLocalizationData?.versionName ??
"-"),
state.apiLocalizationData?.versionName ?? "-"),
),
const SizedBox(width: 12),
Button(
onPressed: onSwitchFile,
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 3),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
child: Icon(FluentIcons.switch_widget),
)),
if (state.customizeGlobalIni != null) ...[
@ -89,23 +85,19 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
model.setCustomizeGlobalIni(null);
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 3),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
child: Icon(FluentIcons.delete),
)),
]
],
)),
Text(S.current.home_localization_advanced_title_msg(
state.serverGlobalIniLines,
state.p4kGlobalIniLines)),
Text(S.current
.home_localization_advanced_title_msg(state.serverGlobalIniLines, state.p4kGlobalIniLines)),
const SizedBox(width: 32),
Button(
child: Padding(
padding: const EdgeInsets.only(
left: 12, right: 12, top: 4, bottom: 4),
child: Text(S.current
.home_localization_advanced_action_install),
padding: const EdgeInsets.only(left: 12, right: 12, top: 4, bottom: 4),
child: Text(S.current.home_localization_advanced_action_install),
),
onPressed: () {
model.onInstall(context);
@ -113,19 +105,13 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
const SizedBox(width: 12),
],
),
Expanded(
child:
_makeBody(context, homeUIState, state, ref, model)),
Expanded(child: _makeBody(context, homeUIState, state, ref, model)),
]
],
));
}
Widget _makeBody(
BuildContext context,
HomeUIModelState homeUIState,
AdvancedLocalizationUIState state,
WidgetRef ref,
Widget _makeBody(BuildContext context, HomeUIModelState homeUIState, AdvancedLocalizationUIState state, WidgetRef ref,
AdvancedLocalizationUIModel model) {
return AlignedGridView.count(
crossAxisCount: 4,
@ -134,109 +120,104 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
padding: const EdgeInsets.all(12),
itemBuilder: (BuildContext context, int index) {
final item = state.classMap!.values.elementAt(index);
return Container(
padding: const EdgeInsets.only(top: 6, bottom: 12),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .05),
borderRadius: BorderRadius.circular(4),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
onPressed:
item.isWorking ? null : () => _showContent(context, item),
icon: Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
Expanded(
child: Text(
"${item.className}",
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.start,
)),
Text(
"${item.valuesMap.length}",
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: .6),
),
),
const SizedBox(width: 6),
Icon(
FluentIcons.chevron_right,
color: Colors.white.withValues(alpha: .6),
size: 16,
),
],
),
),
),
Container(
margin: const EdgeInsets.only(top: 6, bottom: 12),
width: MediaQuery.of(context).size.width,
height: 1,
color: Colors.white.withValues(alpha: .1),
),
if (item.isWorking)
Column(
children: [
makeLoading(context),
const SizedBox(height: 6),
Text(
S.current.home_localization_advanced_action_mod_change),
],
)
else ...[
Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
Expanded(
child: Text(S
.current.home_localization_advanced_action_mode)),
ComboBox(
value: item.mode,
items: [
for (final type
in AppAdvancedLocalizationClassKeysDataMode
.values)
ComboBoxItem(
value: type,
child: Text(state.typeNames[type] ?? "-"),
),
],
onChanged: item.lockMod
? null
: (v) => model.onChangeMod(item,
v as AppAdvancedLocalizationClassKeysDataMode),
),
],
),
),
const SizedBox(height: 6),
SizedBox(
height: 180,
child: SuperListView.builder(
itemCount: item.valuesMap.length,
return GridItemAnimator(
index: index,
child: Container(
padding: const EdgeInsets.only(top: 6, bottom: 12),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .05),
borderRadius: BorderRadius.circular(4),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
onPressed: item.isWorking ? null : () => _showContent(context, item),
icon: Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
itemBuilder: (BuildContext context, int index) {
final itemKey = item.valuesMap.keys.elementAt(index);
return Text(
"${item.valuesMap[itemKey]}",
maxLines: 1,
style: const TextStyle(
fontSize: 12,
overflow: TextOverflow.ellipsis,
child: Row(
children: [
Expanded(
child: Text(
"${item.className}",
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.start,
)),
Text(
"${item.valuesMap.length}",
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: .6),
),
),
);
},
const SizedBox(width: 6),
Icon(
FluentIcons.chevron_right,
color: Colors.white.withValues(alpha: .6),
size: 16,
),
],
),
),
),
Container(
margin: const EdgeInsets.only(top: 6, bottom: 12),
width: MediaQuery.of(context).size.width,
height: 1,
color: Colors.white.withValues(alpha: .1),
),
if (item.isWorking)
Column(
children: [
makeLoading(context),
const SizedBox(height: 6),
Text(S.current.home_localization_advanced_action_mod_change),
],
)
else ...[
Padding(
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(
children: [
Expanded(child: Text(S.current.home_localization_advanced_action_mode)),
ComboBox(
value: item.mode,
items: [
for (final type in AppAdvancedLocalizationClassKeysDataMode.values)
ComboBoxItem(
value: type,
child: Text(state.typeNames[type] ?? "-"),
),
],
onChanged: item.lockMod
? null
: (v) => model.onChangeMod(item, v as AppAdvancedLocalizationClassKeysDataMode),
),
],
),
),
const SizedBox(height: 6),
SizedBox(
height: 180,
child: SuperListView.builder(
itemCount: item.valuesMap.length,
padding: const EdgeInsets.only(left: 12, right: 12),
itemBuilder: (BuildContext context, int index) {
final itemKey = item.valuesMap.keys.elementAt(index);
return Text(
"${item.valuesMap[itemKey]}",
maxLines: 1,
style: const TextStyle(
fontSize: 12,
overflow: TextOverflow.ellipsis,
),
);
},
),
),
],
],
],
),
),
);
},
@ -244,8 +225,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
);
}
_showContent(
BuildContext context, AppAdvancedLocalizationClassKeysData item) {
_showContent(BuildContext context, AppAdvancedLocalizationClassKeysData item) {
showDialog(
context: context,
builder: (BuildContext context) {
@ -282,8 +262,7 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
const SizedBox(
width: 24,
),
Text(S.current.home_localization_advanced_title_preview(
item.className ?? "-")),
Text(S.current.home_localization_advanced_title_preview(item.className ?? "-")),
],
),
content: textData.value.isEmpty
@ -295,13 +274,10 @@ class AdvancedLocalizationUI extends HookConsumerWidget {
),
child: CodeEditor(
readOnly: true,
controller:
CodeLineEditingController.fromText(textData.value),
controller: CodeLineEditingController.fromText(textData.value),
style: CodeEditorStyle(
codeTheme: CodeHighlightTheme(
languages: {
'ini': CodeHighlightThemeMode(mode: langIni)
},
languages: {'ini': CodeHighlightThemeMode(mode: langIni)},
theme: vs2015Theme,
),
),

View File

@ -30,8 +30,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
return ContentDialog(
title: makeTitle(context, model, state),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .7,
minHeight: MediaQuery.of(context).size.height * .9),
maxWidth: MediaQuery.of(context).size.width * .7, minHeight: MediaQuery.of(context).size.height * .9),
content: Padding(
padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
child: SingleChildScrollView(
@ -40,18 +39,15 @@ class LocalizationDialogUI extends HookConsumerWidget {
AnimatedSize(
duration: const Duration(milliseconds: 130),
child: state.patchStatus?.key == true &&
state.patchStatus?.value ==
S.current.home_action_info_game_built_in
state.patchStatus?.value == S.current.home_action_info_game_built_in
? Padding(
padding: const EdgeInsets.only(bottom: 12),
child: InfoBar(
title: Text(S.current.home_action_info_warning),
content: Text(S.current
.localization_info_machine_translation_warning),
content: Text(S.current.localization_info_machine_translation_warning),
severity: InfoBarSeverity.info,
style: InfoBarThemeData(decoration: (severity) {
return const BoxDecoration(
color: Color.fromRGBO(155, 7, 7, 1.0));
return const BoxDecoration(color: Color.fromRGBO(155, 7, 7, 1.0));
}, iconColor: (severity) {
return Colors.white;
}),
@ -65,10 +61,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: InfoBar(
title: Text(S.current
.home_localization_ptu_advanced_localization_tip_title),
content: Text(S.current
.home_localization_ptu_advanced_localization_tip_title_info),
title: Text(S.current.home_localization_ptu_advanced_localization_tip_title),
content: Text(S.current.home_localization_ptu_advanced_localization_tip_title_info),
severity: InfoBarSeverity.info,
style: InfoBarThemeData(decoration: (severity) {
return BoxDecoration(color: Colors.orange);
@ -89,9 +83,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [
Center(
child: Text(S.current.localization_info_enabled(
LocalizationUIModel.languageSupport[
state.selectedLanguage] ??
"")),
LocalizationUIModel.languageSupport[state.selectedLanguage] ?? "")),
),
const Spacer(),
ToggleSwitch(
@ -109,20 +101,15 @@ class LocalizationDialogUI extends HookConsumerWidget {
Text(S.current.localization_info_installed_version(
"${state.patchStatus?.value ?? ""} ${(state.isInstalledAdvanced ?? false) ? S.current.home_localization_msg_version_advanced : ""}")),
SizedBox(width: 24),
if (state
.installedCommunityInputMethodSupportVersion !=
null)
if (state.installedCommunityInputMethodSupportVersion != null)
Text(
S.current
.input_method_community_input_method_support_version(
state.installedCommunityInputMethodSupportVersion ??
"?"),
S.current.input_method_community_input_method_support_version(
state.installedCommunityInputMethodSupportVersion ?? "?"),
)
],
),
),
if (state.patchStatus?.value !=
S.current.home_action_info_game_built_in)
if (state.patchStatus?.value != S.current.home_action_info_game_built_in)
Row(
children: [
Button(
@ -133,8 +120,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [
const Icon(FluentIcons.feedback),
const SizedBox(width: 6),
Text(S.current
.localization_action_translation_feedback),
Text(S.current.localization_action_translation_feedback),
],
),
)),
@ -147,8 +133,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
children: [
const Icon(FluentIcons.delete),
const SizedBox(width: 6),
Text(S.current
.localization_action_uninstall_translation),
Text(S.current.localization_action_uninstall_translation),
],
),
)),
@ -167,11 +152,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
else if (state.apiLocalizationData!.isEmpty)
Center(
child: Text(
S.current
.localization_info_no_translation_available,
style: TextStyle(
fontSize: 13,
color: Colors.white.withValues(alpha: .8)),
S.current.localization_info_no_translation_available,
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .8)),
),
)
else
@ -180,9 +162,8 @@ class LocalizationDialogUI extends HookConsumerWidget {
crossAxisSpacing: 12,
mainAxisSpacing: 12,
itemBuilder: (BuildContext context, int index) {
final item = state.apiLocalizationData!.entries
.elementAt(index);
return makeRemoteList(context, model, item, state);
final item = state.apiLocalizationData!.entries.elementAt(index);
return makeRemoteList(context, model, item, state, index);
},
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
@ -198,136 +179,124 @@ class LocalizationDialogUI extends HookConsumerWidget {
);
}
Widget makeRemoteList(BuildContext context, LocalizationUIModel model,
MapEntry<String, ScLocalizationData> item, LocalizationUIState state) {
Widget makeRemoteList(BuildContext context, LocalizationUIModel model, MapEntry<String, ScLocalizationData> item,
LocalizationUIState state, int index) {
final isWorking = state.workingVersion.isNotEmpty;
final isMineWorking = state.workingVersion == item.key;
final isInstalled = state.patchStatus?.value == item.key;
final isItemEnabled = ((item.value.enable ?? false));
final tapDisabled =
isInstalled || isWorking || !isItemEnabled || isMineWorking;
return Tilt(
shadowConfig: const ShadowConfig(maxIntensity: .3),
borderRadius: BorderRadius.circular(7),
disable: tapDisabled,
child: GestureDetector(
onTap: tapDisabled
? null
: () => model.onRemoteInsTall(context, item, state),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: tapDisabled ? .03 : .05),
borderRadius: BorderRadius.circular(7),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${item.value.info}",
style: const TextStyle(fontSize: 19),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_version_number(
item.value.versionName ?? ""),
style: TextStyle(
color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_channel(
item.value.gameChannel ?? ""),
style: TextStyle(
color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_update_time(
item.value.updateAt ?? ""),
style: TextStyle(
color: Colors.white.withValues(alpha: .6)),
),
],
),
),
if (isMineWorking)
const Padding(
padding: EdgeInsets.only(right: 12),
child: ProgressRing(),
)
else ...[
Icon(
isInstalled
? FluentIcons.check_mark
: isItemEnabled
? FluentIcons.download
: FluentIcons.disable_updates,
color: Colors.white.withValues(alpha: .8),
size: 18,
),
const SizedBox(width: 6),
Text(
isInstalled
? S.current.localization_info_installed
: (isItemEnabled
? S.current.localization_action_install
: S.current.localization_info_unavailable),
style: TextStyle(
color: Colors.white.withValues(alpha: .8),
final tapDisabled = isInstalled || isWorking || !isItemEnabled || isMineWorking;
return GridItemAnimator(
index: index,
child: Tilt(
shadowConfig: const ShadowConfig(maxIntensity: .3),
borderRadius: BorderRadius.circular(7),
disable: tapDisabled,
child: GestureDetector(
onTap: tapDisabled ? null : () => model.onRemoteInsTall(context, item, state),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: tapDisabled ? .03 : .05),
borderRadius: BorderRadius.circular(7),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${item.value.info}",
style: const TextStyle(fontSize: 19),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_version_number(item.value.versionName ?? ""),
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_channel(item.value.gameChannel ?? ""),
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 4),
Text(
S.current.localization_info_update_time(item.value.updateAt ?? ""),
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
),
],
),
),
const SizedBox(width: 6),
if ((!isInstalled) && isItemEnabled)
Icon(
FluentIcons.chevron_right,
size: 14,
color: Colors.white.withValues(alpha: .6),
if (isMineWorking)
const Padding(
padding: EdgeInsets.only(right: 12),
child: ProgressRing(),
)
]
],
),
if (item.value.note != null) ...[
const SizedBox(height: 6),
Text(
"${item.value.note}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white.withValues(alpha: .4),
fontSize: 13,
),
else ...[
Icon(
isInstalled
? FluentIcons.check_mark
: isItemEnabled
? FluentIcons.download
: FluentIcons.disable_updates,
color: Colors.white.withValues(alpha: .8),
size: 18,
),
const SizedBox(width: 6),
Text(
isInstalled
? S.current.localization_info_installed
: (isItemEnabled
? S.current.localization_action_install
: S.current.localization_info_unavailable),
style: TextStyle(
color: Colors.white.withValues(alpha: .8),
),
),
const SizedBox(width: 6),
if ((!isInstalled) && isItemEnabled)
Icon(
FluentIcons.chevron_right,
size: 14,
color: Colors.white.withValues(alpha: .6),
)
]
],
),
if (item.value.note != null) ...[
const SizedBox(height: 6),
Text(
"${item.value.note}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white.withValues(alpha: .4),
fontSize: 13,
),
),
],
],
],
),
),
),
),
);
}
Widget makeListContainer(
String title, List<Widget> children, BuildContext context,
{List<Widget> actions = const [],
bool gridViewMode = false,
int gridViewCrossAxisCount = 2}) {
Widget makeListContainer(String title, List<Widget> children, BuildContext context,
{List<Widget> actions = const [], bool gridViewMode = false, int gridViewCrossAxisCount = 2}) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: AnimatedSize(
duration: const Duration(milliseconds: 130),
child: Container(
decoration: BoxDecoration(
color: FluentTheme.of(context).cardColor,
borderRadius: BorderRadius.circular(7)),
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor, borderRadius: BorderRadius.circular(7)),
child: Padding(
padding:
const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24),
padding: const EdgeInsets.only(top: 12, bottom: 12, left: 24, right: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -371,8 +340,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
);
}
Widget makeTitle(BuildContext context, LocalizationUIModel model,
LocalizationUIState state) {
Widget makeTitle(BuildContext context, LocalizationUIModel model, LocalizationUIState state) {
return Row(
children: [
IconButton(
@ -401,8 +369,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
ComboBox<String>(
value: state.selectedLanguage,
items: [
for (final lang
in LocalizationUIModel.languageSupport.entries)
for (final lang in LocalizationUIModel.languageSupport.entries)
ComboBoxItem(
value: lang.key,
child: Text(lang.value),
@ -429,8 +396,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
);
}
Widget makeToolsListContainer(BuildContext context, LocalizationUIModel model,
LocalizationUIState state) {
Widget makeToolsListContainer(BuildContext context, LocalizationUIModel model, LocalizationUIState state) {
final toolsMenu = {
"launcher_mod": (
const Icon(FluentIcons.c_plus_plus, size: 24),
@ -469,8 +435,7 @@ class LocalizationDialogUI extends HookConsumerWidget {
case "custom_files":
final sb = await showDialog(
context: context,
builder: (BuildContext context) =>
const LocalizationFromFileDialogUI(),
builder: (BuildContext context) => const LocalizationFromFileDialogUI(),
);
if (sb is (StringBuffer, bool)) {
await model.installFormString(

View File

@ -12,6 +12,7 @@ import 'package:window_manager/window_manager.dart';
import 'about/about_ui.dart';
import 'home/home_ui.dart';
import 'nav/nav_ui.dart';
import 'settings/settings_ui.dart';
import 'tools/tools_ui.dart';
@ -42,8 +43,7 @@ class IndexUI extends HookConsumerWidget {
fit: BoxFit.cover,
),
const SizedBox(width: 12),
Text(S.current.app_index_version_info(
ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")),
Text(S.current.app_index_version_info(ConstConf.appVersion, ConstConf.isMSE ? "" : " Dev")),
],
),
),
@ -77,8 +77,7 @@ class IndexUI extends HookConsumerWidget {
key: Key("NavigationPane_${S.current.app_language_code}"),
selected: curIndex.value,
items: getNavigationPaneItems(curIndex),
size: NavigationPaneSize(
openWidth: S.current.app_language_code.startsWith("zh") ? 64 : 74),
size: NavigationPaneSize(openWidth: S.current.app_language_code.startsWith("zh") ? 64 : 74),
),
paneBodyBuilder: (item, child) {
return item!.body;
@ -95,18 +94,15 @@ class IndexUI extends HookConsumerWidget {
S.current.app_index_menu_tools,
const ToolsUI(),
),
FluentIcons.settings: (
S.current.app_index_menu_settings,
const SettingsUI()
),
FluentIcons.power_apps: ("导航", const NavUI()),
FluentIcons.settings: (S.current.app_index_menu_settings, const SettingsUI()),
FluentIcons.info: (
S.current.app_index_menu_about,
const AboutUI(),
),
};
List<NavigationPaneItem> getNavigationPaneItems(
ValueNotifier<int> curIndexState) {
List<NavigationPaneItem> getNavigationPaneItems(ValueNotifier<int> curIndexState) {
// width = 64
return [
for (final kv in pageMenus.entries)
@ -136,8 +132,7 @@ class IndexUI extends HookConsumerWidget {
}
void _onTapIndexMenu(String value, ValueNotifier<int> curIndexState) {
final pageIndex =
pageMenus.values.toList().indexWhere((element) => element.$1 == value);
final pageIndex = pageMenus.values.toList().indexWhere((element) => element.$1 == value);
curIndexState.value = pageIndex;
}
@ -156,8 +151,7 @@ class IndexUI extends HookConsumerWidget {
color: Colors.red,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.only(
left: 6, right: 6, bottom: 1.5, top: 1.5),
padding: const EdgeInsets.only(left: 6, right: 6, bottom: 1.5, top: 1.5),
child: Text(
"${aria2cState.aria2TotalTaskNum}",
style: const TextStyle(

41
lib/ui/nav/nav_state.dart Normal file
View File

@ -0,0 +1,41 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:starcitizen_doctor/api/udb.dart';
import 'package:starcitizen_doctor/data/nav_api_data.dart';
part 'nav_state.freezed.dart';
part 'nav_state.g.dart';
@freezed
class NavState with _$NavState {
const factory NavState({
List<NavApiDocsItemData>? items,
@Default("") String errorInfo,
}) = _NavState;
}
@riverpod
class Nav extends _$Nav {
bool _mounted = true;
@override
NavState build() {
state = NavState();
loadData(1);
ref.onDispose(() {
_mounted = false;
});
return state;
}
void loadData(int pageNo) async {
if (!_mounted) return;
try {
final r = await UDBNavApi.getNavItems(pageNo: pageNo);
state = state.copyWith(items: r.docs, errorInfo: "");
} catch (e) {
state = state.copyWith(errorInfo: e.toString());
}
}
}

View File

@ -0,0 +1,173 @@
// 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 'nav_state.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 _$NavState {
List<NavApiDocsItemData>? get items => throw _privateConstructorUsedError;
String get errorInfo => throw _privateConstructorUsedError;
/// Create a copy of NavState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$NavStateCopyWith<NavState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $NavStateCopyWith<$Res> {
factory $NavStateCopyWith(NavState value, $Res Function(NavState) then) =
_$NavStateCopyWithImpl<$Res, NavState>;
@useResult
$Res call({List<NavApiDocsItemData>? items, String errorInfo});
}
/// @nodoc
class _$NavStateCopyWithImpl<$Res, $Val extends NavState>
implements $NavStateCopyWith<$Res> {
_$NavStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of NavState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? items = freezed,
Object? errorInfo = null,
}) {
return _then(_value.copyWith(
items: freezed == items
? _value.items
: items // ignore: cast_nullable_to_non_nullable
as List<NavApiDocsItemData>?,
errorInfo: null == errorInfo
? _value.errorInfo
: errorInfo // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$NavStateImplCopyWith<$Res>
implements $NavStateCopyWith<$Res> {
factory _$$NavStateImplCopyWith(
_$NavStateImpl value, $Res Function(_$NavStateImpl) then) =
__$$NavStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<NavApiDocsItemData>? items, String errorInfo});
}
/// @nodoc
class __$$NavStateImplCopyWithImpl<$Res>
extends _$NavStateCopyWithImpl<$Res, _$NavStateImpl>
implements _$$NavStateImplCopyWith<$Res> {
__$$NavStateImplCopyWithImpl(
_$NavStateImpl _value, $Res Function(_$NavStateImpl) _then)
: super(_value, _then);
/// Create a copy of NavState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? items = freezed,
Object? errorInfo = null,
}) {
return _then(_$NavStateImpl(
items: freezed == items
? _value._items
: items // ignore: cast_nullable_to_non_nullable
as List<NavApiDocsItemData>?,
errorInfo: null == errorInfo
? _value.errorInfo
: errorInfo // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$NavStateImpl implements _NavState {
const _$NavStateImpl(
{final List<NavApiDocsItemData>? items, this.errorInfo = ""})
: _items = items;
final List<NavApiDocsItemData>? _items;
@override
List<NavApiDocsItemData>? get items {
final value = _items;
if (value == null) return null;
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
@JsonKey()
final String errorInfo;
@override
String toString() {
return 'NavState(items: $items, errorInfo: $errorInfo)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$NavStateImpl &&
const DeepCollectionEquality().equals(other._items, _items) &&
(identical(other.errorInfo, errorInfo) ||
other.errorInfo == errorInfo));
}
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_items), errorInfo);
/// Create a copy of NavState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$NavStateImplCopyWith<_$NavStateImpl> get copyWith =>
__$$NavStateImplCopyWithImpl<_$NavStateImpl>(this, _$identity);
}
abstract class _NavState implements NavState {
const factory _NavState(
{final List<NavApiDocsItemData>? items,
final String errorInfo}) = _$NavStateImpl;
@override
List<NavApiDocsItemData>? get items;
@override
String get errorInfo;
/// Create a copy of NavState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$NavStateImplCopyWith<_$NavStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'nav_state.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$navHash() => r'2019b3f675fbaec4be794049d900bf2dcc8d5e37';
/// See also [Nav].
@ProviderFor(Nav)
final navProvider = AutoDisposeNotifierProvider<Nav, NavState>.internal(
Nav.new,
name: r'navProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$navHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$Nav = AutoDisposeNotifier<NavState>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

203
lib/ui/nav/nav_ui.dart Normal file
View File

@ -0,0 +1,203 @@
import 'dart:ui';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/gestures.dart' show TapGestureRecognizer;
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_tilt/flutter_tilt.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/ui/nav/nav_state.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:starcitizen_doctor/widgets/widgets.dart';
class NavUI extends HookConsumerWidget {
const NavUI({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Column(
children: [
Expanded(
child: buildBody(context, ref),
),
SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text.rich(
TextSpan(children: [
TextSpan(text: "*对应链接指向的服务由第三方提供,我们不对其做任何担保,请用户自行判断使用风险 | "),
TextSpan(text: "网站导航数据由"),
TextSpan(
text: " 42kit ",
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
launchUrlString("https://42kit.citizenwiki.cn/nav");
},
),
TextSpan(text: "提供"),
]),
style: TextStyle(
fontSize: 13,
color: Colors.white.withValues(alpha: .6),
),
),
SizedBox(width: 12),
],
),
],
),
);
}
Widget buildBody(BuildContext context, WidgetRef ref) {
final data = ref.watch(navProvider);
if (data.errorInfo.isNotEmpty) {
return Center(
child: Text(
data.errorInfo,
style: TextStyle(color: Colors.red),
),
);
}
if (data.items == null) {
return const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ProgressRing(),
SizedBox(height: 12),
Text("正在获取数据..."),
],
));
}
return MasonryGridView.count(
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemCount: data.items!.length,
padding: EdgeInsets.only(left: 12, right: 12, bottom: 12),
itemBuilder: (BuildContext context, int index) {
const itemHeight = 160.0;
final item = data.items![index];
final itemName = item.name;
final itemImage = item.image.url;
return GridItemAnimator(
index: index,
child: GestureDetector(
onTap: () {
launchUrlString(item.link);
},
child: Tilt(
shadowConfig: const ShadowConfig(maxIntensity: .3),
borderRadius: BorderRadius.circular(12),
clipBehavior: Clip.hardEdge,
child: SizedBox(
height: itemHeight,
width: double.infinity,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
clipBehavior: Clip.hardEdge,
child: Stack(
children: [
Center(
child: CacheNetImage(
height: itemHeight,
width: double.infinity,
url: itemImage,
fit: BoxFit.fitWidth,
),
),
Container(
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: .55),
),
),
ClipRect(
clipBehavior: Clip.hardEdge,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 15.0, sigmaY: 15.0),
blendMode: BlendMode.srcOver,
child: SizedBox(
width: double.infinity,
height: itemHeight,
),
),
),
Container(
decoration: BoxDecoration(),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: CacheNetImage(
url: itemImage,
height: 48,
width: 48,
fit: BoxFit.cover,
),
),
SizedBox(width: 12),
Flexible(
child: Text(
itemName,
style: TextStyle(
fontSize: 18,
),
),
),
],
),
SizedBox(height: 12),
Expanded(
child: Text(
item.abstract_,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.white.withValues(alpha: .75)),
),
),
Row(
children: [
for (var value in item.tags)
Container(
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: .6),
borderRadius: BorderRadius.circular(12)),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 3),
margin: EdgeInsets.only(right: 6),
child: Text(
value.name,
style: TextStyle(
fontSize: 13,
color: Colors.white,
),
),
)
],
)
],
),
),
],
),
),
),
),
),
);
},
);
}
}

View File

@ -40,13 +40,9 @@ class ToolsUI extends HookConsumerWidget {
),
const SizedBox(width: 12),
Button(
onPressed: state.working
? null
: () =>
model.loadToolsCard(context, skipPathScan: false),
onPressed: state.working ? null : () => model.loadToolsCard(context, skipPathScan: false),
child: const Padding(
padding: EdgeInsets.only(
top: 30, bottom: 30, left: 12, right: 12),
padding: EdgeInsets.only(top: 30, bottom: 30, left: 12, right: 12),
child: Icon(FluentIcons.refresh),
),
),
@ -75,90 +71,86 @@ class ToolsUI extends HookConsumerWidget {
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemCount: (state.isItemLoading)
? state.items.length + 1
: state.items.length,
itemCount: (state.isItemLoading) ? state.items.length + 1 : state.items.length,
shrinkWrap: true,
itemBuilder: (context, index) {
if (index == state.items.length) {
return Container(
width: 300,
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: FluentTheme.of(context).cardColor,
),
child: makeLoading(context));
return GridItemAnimator(
index: index,
child: Container(
width: 300,
height: 160,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: FluentTheme.of(context).cardColor,
),
child: makeLoading(context)),
);
}
final item = state.items[index];
return Container(
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: FluentTheme.of(context).cardColor,
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color:
Colors.white.withValues(alpha: .2),
borderRadius:
BorderRadius.circular(1000)),
child: Padding(
padding: const EdgeInsets.all(12),
child: item.icon,
return GridItemAnimator(
index: index,
child: Container(
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: FluentTheme.of(context).cardColor,
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .2),
borderRadius: BorderRadius.circular(1000)),
child: Padding(
padding: const EdgeInsets.all(12),
child: item.icon,
),
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
item.name,
style: const TextStyle(fontSize: 16),
)),
const SizedBox(width: 12),
],
),
const SizedBox(height: 12),
Text(
item.infoString,
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 12),
Row(
children: [
const Spacer(),
Button(
onPressed: state.working
? null
: item.onTap == null
? null
: () {
try {
item.onTap?.call();
} catch (e) {
showToast(
context,
S.current
.tools_info_processing_failed(
e));
}
},
child: const Padding(
padding: EdgeInsets.all(6),
child: Icon(FluentIcons.play),
const SizedBox(width: 8),
Expanded(
child: Text(
item.name,
style: const TextStyle(fontSize: 16),
)),
const SizedBox(width: 12),
],
),
const SizedBox(height: 12),
Text(
item.infoString,
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
const SizedBox(height: 12),
Row(
children: [
const Spacer(),
Button(
onPressed: state.working
? null
: item.onTap == null
? null
: () {
try {
item.onTap?.call();
} catch (e) {
showToast(context, S.current.tools_info_processing_failed(e));
}
},
child: const Padding(
padding: EdgeInsets.all(6),
child: Icon(FluentIcons.play),
),
),
),
],
)
],
],
)
],
),
),
),
);
@ -188,8 +180,7 @@ class ToolsUI extends HookConsumerWidget {
);
}
Widget makeGamePathSelect(
BuildContext context, ToolsUIModel model, ToolsUIState state) {
Widget makeGamePathSelect(BuildContext context, ToolsUIModel model, ToolsUIState state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -223,8 +214,7 @@ class ToolsUI extends HookConsumerWidget {
),
onPressed: () {
if (state.scInstalledPath.trim().isEmpty) {
showToast(context,
S.current.tools_action_info_star_citizen_not_found);
showToast(context, S.current.tools_action_info_star_citizen_not_found);
return;
}
model.openDir(state.scInstalledPath);
@ -233,8 +223,7 @@ class ToolsUI extends HookConsumerWidget {
);
}
Widget makeGameLauncherPathSelect(
BuildContext context, ToolsUIModel model, ToolsUIState state) {
Widget makeGameLauncherPathSelect(BuildContext context, ToolsUIModel model, ToolsUIState state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -268,10 +257,7 @@ class ToolsUI extends HookConsumerWidget {
),
onPressed: () {
if (state.scInstalledPath.trim().isEmpty) {
showToast(
context,
S.current
.tools_rsi_launcher_enhance_msg_error_launcher_notfound);
showToast(context, S.current.tools_rsi_launcher_enhance_msg_error_launcher_notfound);
return;
}
model.openDir(state.rsiLauncherInstalledPath);

View File

@ -6,7 +6,7 @@ part of 'tools_ui_model.dart';
// RiverpodGenerator
// **************************************************************************
String _$toolsUIModelHash() => r'cd72f7833fa5696baf9022d16d10d7951387df7e';
String _$toolsUIModelHash() => r'c8830e26df6c0ee572dd5e78c4ccef3317f8b4e6';
/// See also [ToolsUIModel].
@ProviderFor(ToolsUIModel)