mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-06-28 02:04:44 +08:00
feat: Donation page
This commit is contained in:
@ -1,7 +1,9 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
@ -16,86 +18,414 @@ class AboutUI extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isTipTextCn = useState(false);
|
||||
final pageCtrl = usePageController();
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Spacer(),
|
||||
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"),
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
const SizedBox(height: 12),
|
||||
Button(
|
||||
onPressed: () => _onCheckUpdate(context, ref),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(S.current.about_check_update),
|
||||
)),
|
||||
const SizedBox(height: 32),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(24),
|
||||
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)),
|
||||
return PageView(
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: pageCtrl,
|
||||
children: [
|
||||
_makeAbout(context, ref, isTipTextCn, pageCtrl),
|
||||
_makeDonate(context, ref, pageCtrl),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _makeAbout(BuildContext context, WidgetRef ref,
|
||||
ValueNotifier<bool> isTipTextCn, PageController pageCtrl) {
|
||||
return Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Spacer(),
|
||||
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"),
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
const SizedBox(height: 12),
|
||||
Button(
|
||||
onPressed: () => _onCheckUpdate(context, ref),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(S.current.about_check_update),
|
||||
)),
|
||||
const SizedBox(height: 32),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(24),
|
||||
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)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
makeAnalyticsWidget(context),
|
||||
const SizedBox(height: 24),
|
||||
makeLinksRow(),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * .35,
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withValues(alpha: .06),
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: IconButton(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Text(
|
||||
isTipTextCn.value ? tipTextCN : tipTextEN,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white.withValues(alpha: .9)),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
isTipTextCn.value = !isTipTextCn.value;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 12,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: makeNavButton(pageCtrl, 1),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _makeDonate(
|
||||
BuildContext context, WidgetRef ref, PageController pageCtrl) {
|
||||
final donationTypeNotifier = useState('alipay');
|
||||
final bubbleMessages = [
|
||||
S.current.support_dev_thanks_message,
|
||||
S.current.support_dev_referral_code_message,
|
||||
];
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
makeNavButton(pageCtrl, 0),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
Text(
|
||||
S.current.support_dev_title,
|
||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// 聊天头像和气泡消息
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: CacheNetImage(
|
||||
url:
|
||||
"https://git.scbox.xkeyc.cn/avatars/56a93334e892ba48f4fab453b8205624d661e4f7748cdb52bed47e5dc0c85de5?size=512",
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (var i = 0; i < bubbleMessages.length; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: SelectionArea(
|
||||
child: ChatBubble(message: bubbleMessages[i])),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// 捐赠方式选择
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_donationMethodButton(
|
||||
context: context,
|
||||
title: 'WeChat',
|
||||
icon: FontAwesomeIcons.weixin,
|
||||
isSelected: donationTypeNotifier.value == 'wechat',
|
||||
color: const Color(0xFF07C160),
|
||||
onTap: () => donationTypeNotifier.value = 'wechat',
|
||||
),
|
||||
_donationMethodButton(
|
||||
context: context,
|
||||
title: 'AliPay',
|
||||
icon: FontAwesomeIcons.alipay,
|
||||
isSelected: donationTypeNotifier.value == 'alipay',
|
||||
color: const Color(0xFF1677FF),
|
||||
onTap: () => donationTypeNotifier.value = 'alipay',
|
||||
),
|
||||
_donationMethodButton(
|
||||
context: context,
|
||||
title: 'QQ',
|
||||
icon: FontAwesomeIcons.qq,
|
||||
isSelected: donationTypeNotifier.value == 'qq',
|
||||
color: const Color(0xFF12B7F5),
|
||||
onTap: () => donationTypeNotifier.value = 'qq',
|
||||
),
|
||||
_donationMethodButton(
|
||||
context: context,
|
||||
title: 'aUEC',
|
||||
icon: FontAwesomeIcons.gamepad,
|
||||
isSelected: donationTypeNotifier.value == 'uec',
|
||||
color: const Color(0xFFFFD700),
|
||||
onTap: () => donationTypeNotifier.value = 'uec',
|
||||
),
|
||||
_donationMethodButton(
|
||||
context: context,
|
||||
title: 'GitHub',
|
||||
icon: FontAwesomeIcons.github,
|
||||
isSelected: donationTypeNotifier.value == 'github',
|
||||
color: Colors.white,
|
||||
onTap: () => donationTypeNotifier.value = 'github',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 二维码显示区域
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 300),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
child: _buildDonationQRCode(donationTypeNotifier.value, context),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 48),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _donationMethodButton({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required bool isSelected,
|
||||
required Color color,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Padding(
|
||||
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)),
|
||||
),
|
||||
onPressed: onTap,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, color: color, size: 24),
|
||||
const SizedBox(height: 4),
|
||||
Text(title, style: TextStyle(fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDonationQRCode(String type, BuildContext context) {
|
||||
// 处理 GitHub 特殊情况
|
||||
if (type == 'github') {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
key: ValueKey('github'),
|
||||
children: [
|
||||
SizedBox(height: 28),
|
||||
const Icon(FontAwesomeIcons.github, size: 64),
|
||||
const SizedBox(height: 32),
|
||||
Text(S.current.support_dev_github_star_message),
|
||||
const SizedBox(height: 32),
|
||||
Button(
|
||||
onPressed: () {
|
||||
launchUrlString("https://github.com/StarCitizenToolBox/app");
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(FontAwesomeIcons.github),
|
||||
const SizedBox(width: 12),
|
||||
Text(S.current.support_dev_github_star_button),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
makeAnalyticsWidget(context),
|
||||
const SizedBox(height: 24),
|
||||
makeLinksRow(),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * .35,
|
||||
decoration: BoxDecoration(
|
||||
color: FluentTheme.of(context)
|
||||
.cardColor
|
||||
.withValues(alpha: .06),
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: IconButton(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Text(
|
||||
isTipTextCn.value ? tipTextCN : tipTextEN,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white.withValues(alpha: .9)),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
isTipTextCn.value = !isTipTextCn.value;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// UEC 游戏内捐赠也特殊处理
|
||||
if (type == 'uec') {
|
||||
return Column(
|
||||
key: ValueKey('uec'),
|
||||
children: [
|
||||
Image.asset("assets/sc_logo.png", width: 128, height: 128),
|
||||
const SizedBox(height: 16),
|
||||
Text(S.current.support_dev_in_game_currency_title,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
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)),
|
||||
const SizedBox(width: 12),
|
||||
Button(
|
||||
onPressed: () {
|
||||
// ${S.current.support_dev_copy_button}游戏ID到剪贴板
|
||||
Clipboard.setData(ClipboardData(text: "xkeyC"));
|
||||
showToast(context, S.current.support_dev_in_game_id_copied);
|
||||
},
|
||||
child: Text(S.current.support_dev_copy_button),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 22),
|
||||
Text(
|
||||
S.current.support_dev_in_game_currency_message,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 其他支付方式显示二维码
|
||||
String qrData;
|
||||
String title;
|
||||
|
||||
switch (type) {
|
||||
case 'alipay':
|
||||
qrData = DonationQrCodeData.alipay;
|
||||
title = S.current.support_dev_alipay;
|
||||
break;
|
||||
case 'wechat':
|
||||
qrData = DonationQrCodeData.wechat;
|
||||
title = S.current.support_dev_wechat;
|
||||
break;
|
||||
|
||||
case 'qq':
|
||||
qrData = DonationQrCodeData.qq;
|
||||
title = "QQ";
|
||||
break;
|
||||
default:
|
||||
qrData = "";
|
||||
title = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return Column(
|
||||
key: ValueKey(type),
|
||||
children: [
|
||||
Text(title,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
width: 200,
|
||||
height: 200,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: QrImageView(data: qrData),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
S.current.support_dev_donation_disclaimer,
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeNavButton(PageController pageCtrl, int pageIndex) {
|
||||
return IconButton(
|
||||
icon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
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),
|
||||
],
|
||||
),
|
||||
onPressed: () => pageCtrl.animateToPage(pageIndex,
|
||||
duration: const Duration(milliseconds: 300), curve: Curves.ease),
|
||||
);
|
||||
}
|
||||
|
||||
@ -285,3 +615,38 @@ class AboutUI extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChatBubble extends StatelessWidget {
|
||||
final String message;
|
||||
|
||||
const ChatBubble({super.key, required this.message});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
FluentTheme.of(context).accentColor.withAlpha((255.0 * .2).round()),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(4),
|
||||
topRight: Radius.circular(18),
|
||||
bottomLeft: Radius.circular(18),
|
||||
bottomRight: Radius.circular(18),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
message,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DonationQrCodeData {
|
||||
static const alipay = "https://qr.alipay.com/tsx16308c4uai0ticmz4j96";
|
||||
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";
|
||||
}
|
||||
|
Reference in New Issue
Block a user