初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/images.dart';
import '../../hj_model/splash/ads_model.dart';
import '../../hj_utils/screen.dart';
import '../../tools_base/banner/ads_banner_widget.dart';
import '../../tools_base/banner/ads_item.dart';
class AppCenterDialog extends StatefulWidget {
final List<AdsInfoModel>? appList;
final List<AdsInfoModel>? adsList;
final VoidCallback? callback;
const AppCenterDialog({super.key, this.appList, this.callback, this.adsList});
@override
State<AppCenterDialog> createState() => _AppCenterDialogState();
}
class _AppCenterDialogState extends State<AppCenterDialog> {
bool _showClose = false;
List<AdsInfoModel> get _appList => widget.appList ?? [];
/// 网格高度上限 = 4 行 + 底部留白,超出滚动。数字跟下面 gridDelegate 对应,改一处要同步。
/// 别用「首帧 shrinkWrap 量高度、次帧再改成固定高」那套——两帧布局不同,弹窗又居中,打开瞬间会跳
double get _maxGridHeight {
final itemWidth = (Get.width - 32 * 2 - 12 * 3) / 4;
return itemWidth * 93 / 68 * 4 + 16 * 3 + 25;
}
@override
void initState() {
super.initState();
// 1 秒后再显示关闭按钮,防误触
Future.delayed(const Duration(milliseconds: 1000), () {
if (mounted) setState(() => _showClose = true);
});
}
@override
Widget build(BuildContext context) {
return Material(
color: Colors.black.withValues(alpha: 0.6),
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 32),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (widget.adsList?.isNotEmpty == true)
Container(
margin: const EdgeInsets.only(bottom: 24),
child: AdsBannerWidget(
widget.adsList,
width: 329,
height: 88,
autoPlayMs: 5000,
),
),
//Flexible 兜住矮屏:内容再多也不会顶破屏幕
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: _maxGridHeight),
child: GridView.builder(
padding: const EdgeInsets.only(bottom: 25),
shrinkWrap: true, //不足 4 行时按内容收,不留空
physics: const ClampingScrollPhysics(),
itemCount: _appList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 12,
mainAxisSpacing: 16,
childAspectRatio: 68 / 93,
),
itemBuilder: (context, index) {
return AdsItem(
adInfo: _appList[index],
);
},
),
),
),
32.sizeBoxH,
//一直挂在树上只切显隐:等 _showClose 才 build 的话,asset 解码前图高是 0,
//会先塌一下再弹回来,居中布局跟着抖
Opacity(
opacity: _showClose ? 1 : 0,
child: IgnorePointer(
ignoring: !_showClose,
child: GestureDetector(
onTap: () => Get.back(),
child: Image.asset("close_button.png".commonImgPath,
width: 32, height: 32),
),
),
),
],
),
),
),
);
}
}
+145
View File
@@ -0,0 +1,145 @@
import 'package:easy_rich_text/easy_rich_text.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/config/config.dart';
import 'package:hgdj/hj_utils/screen.dart';
import 'package:hgdj/routers/jump_router.dart';
import 'package:hgdj/tools_base/widget/common_dialog.dart';
class LineErrorDialog extends StatelessWidget {
static Future show({Function? callback}) {
return Get.dialog(
LineErrorDialog(callback: callback),
barrierDismissible: false,
);
}
final Function? callback;
const LineErrorDialog({super.key, this.callback});
String get url => '91porn01.cc';
@override
Widget build(BuildContext context) {
return CommonDialog(
//线路异常必须让用户处理,Get.dialog 的 barrierDismissible 到不了这层
barrierDismissible: false,
canTapClose: false,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'系统提示',
style: TextStyle(
color: Colors.white.withValues(alpha: .9),
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
12.sizeBoxH,
0.5.line,
12.sizeBoxH,
Row(
children: [
Text(
"APP线路检测失败",
style: TextStyle(
color: Color(0xffF52C56),
fontSize: 12,
),
),
],
),
12.sizeBoxH,
Container(
alignment: Alignment.centerLeft,
child: const Text(
"解决方案:",
style: TextStyle(
color: Color(0xff656565),
fontSize: 12,
fontWeight: FontWeight.w500),
),
),
12.sizeBoxH,
EasyRichText(
'''1.请检测您的网络是否正常,手机是否开了VPN或10分钟后再尝试访问。
2.前往官网地址 $url下载最新版
3.前往Telegram${Config.appName}官方交流群
''',
defaultStyle:
TextStyle(fontSize: 12, color: Color(0xff656565), height: 1.8),
patternList: [
EasyRichTextPattern(
targetString: url,
style: TextStyle(color: AppColors.primaryHighColor),
recognizer: TapGestureRecognizer()
..onTap = () {
launchUrlToWeb('https://$url');
},
),
],
),
18.sizeBoxH,
SizedBox(
height: 38,
child: Row(
children: [
GestureDetector(
onTap: () async {
Get.back();
callback?.call();
},
child: Container(
width: 96,
height: 42,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: Colors.white.withValues(alpha: .1),
),
child: Text(
"切换路线",
style: TextStyle(
color: Color(0xff989898),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
),
12.sizeBoxW,
Expanded(
child: GestureDetector(
onTap: () {
launchUrlToWeb('https://t.me/hougong9527');
},
child: Container(
height: 42,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: AppColors.actionRed,
),
child: const Text(
"前往群聊",
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
)
],
),
)
],
),
);
}
}
+91
View File
@@ -0,0 +1,91 @@
// ignore_for_file: prefer_const_constructors, use_key_in_widget_constructors, use_full_hex_values_for_flutter_colors, sized_box_for_whitespace, unnecessary_import
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/images.dart';
import 'package:hgdj/hj_model/splash/ads_model.dart';
import 'package:hgdj/hj_utils/screen.dart';
import '../../tools_base/banner/ads_item.dart';
/// 系统公告--图片公告
class NoticeImageDialog extends StatelessWidget {
final AdsInfoModel? model;
NoticeImageDialog({this.model});
@override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AdsItem(
adInfo: model!,
showType: AdShowType.img,
aspectRatio: 6 / 8,
),
20.sizeBoxH,
InkWell(
enableFeedback: false,
onTap: () => Get.back(result: false),
child: Image.asset("close_button.png".commonImgPath, width: 32),
)
],
),
),
),
);
}
}
/// 系统公告--上下双排图片公告
class SystemImagesDialog extends StatelessWidget {
final List<AdsInfoModel>? ads;
SystemImagesDialog({this.ads});
@override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AspectRatio(
aspectRatio: 300 / 169,
child: AdsItem(
adInfo: ads![index],
showType: AdShowType.img,
aspectRatio: 6 / 8,
),
);
},
separatorBuilder: (context, index) => 6.sizeBoxH,
itemCount: ads?.length ?? 0,
),
20.sizeBoxH,
InkWell(
enableFeedback: false,
onTap: () => Get.back(result: false),
child: Image.asset("close_button.png".commonImgPath, width: 32),
)
],
),
),
),
);
}
}
+85
View File
@@ -0,0 +1,85 @@
// ignore_for_file: prefer_const_constructors, use_key_in_widget_constructors
import 'package:easy_rich_text/easy_rich_text.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/hj_utils/screen.dart';
import 'package:hgdj/tools_base/widget/common_dialog.dart';
import '../../hj_model/splash/ads_model.dart';
/// 系统公告--文字公告
class NoticeTextDialog extends StatelessWidget {
const NoticeTextDialog({
this.content,
this.addr,
this.wordBean,
});
final AnnounceInfoBean? wordBean;
final String? content;
final String? addr;
@override
Widget build(BuildContext context) {
return CommonDialog(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
wordBean?.title ?? "官方公告",
style: TextStyle(
fontSize: 20,
color: Colors.white.withValues(alpha: 0.9),
fontWeight: FontWeight.w500,
),
),
12.sizeBoxH,
0.5.line,
12.sizeBoxH,
SizedBox(
height: 220,
child: SingleChildScrollView(
child: EasyRichText(
content ?? "",
defaultStyle: TextStyle(
color: Color(0xff656565), fontSize: 12, height: 25 / 12),
patternList: [
EasyRichTextPattern(
targetString:
'(http(s)?:\\/\\/.)?(www\\.)?[-a-zA-Z0-9:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)|https:\/\/t\\.me\/[0-9A-Za-z]{2,256}',
urlType: 'web',
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.blue,
),
),
],
),
),
),
12.sizeBoxH,
InkWell(
enableFeedback: false,
onTap: () => Get.back(),
child: Container(
alignment: Alignment.center,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: AppColors.actionRed),
child: Text(
"我知道了",
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500),
),
),
),
],
),
);
}
}
+260
View File
@@ -0,0 +1,260 @@
// ignore_for_file: depend_on_referenced_packages
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/hj_utils/install_util.dart';
import 'package:hgdj/hj_utils/screen.dart';
import 'package:hgdj/tools_base/toast.dart';
import 'package:hgdj/tools_base/widget/common_dialog.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import '../../hj_model/splash/domain_source_model.dart';
import '../../hj_page/mine/identity/mine_identity_page.dart';
import '../../routers/jump_router.dart';
import '../../tools_base/net/load_apk/dio_cli.dart';
import '../../tools_base/net/load_apk/dio_slice_downloader.dart';
import '../../track_event_manager/device_service.dart';
//升级版本对话框
class UpdateDialog extends StatefulWidget {
static Future show(CheckVersionInfo phoneBean) async {
return await Get.dialog(
UpdateDialog(updateInfo: phoneBean),
barrierDismissible: false,
);
}
final CheckVersionInfo updateInfo;
const UpdateDialog({super.key, required this.updateInfo});
@override
State<UpdateDialog> createState() => _UpdateDialog();
}
class _UpdateDialog extends State<UpdateDialog> {
bool isDownloading = false; // 下载中:既防重入,也决定进度条露不露
var progress = .0;
String apkPath = "";
/// 点「立即升级」:iOS 跳商店,Android 下载 apk,下完再点就是安装
void _onUpdate() async {
//iOS 没有下载安装 apk 那一套,直接跳 App Store;进度条也就不用起(isDownloading 不置位)
if (Platform.isIOS) {
final iosUrl = widget.updateInfo.iosUrl ?? '';
iosUrl.isEmpty ? showToast("暂无更新地址,请稍后再试") : await launchUrlToWeb(iosUrl);
return;
}
if (progress == 1.0) {
await InstallUtil.installApk(apkPath);
return;
}
if (isDownloading) {
return;
}
//开始下载
isDownloading = true;
String devId = DeviceInfoService.deviceId;
Clipboard.setData(ClipboardData(text: "***$devId^^^"));
_downloadApk(widget.updateInfo.url ?? "");
setState(() {});
}
@override
Widget build(BuildContext context) {
return CommonDialog(
//强更场景要挡住点空白关闭,Get.dialog 的 barrierDismissible 到不了这层
barrierDismissible: false,
canTapClose: false,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'系统升级',
style: TextStyle(
color: Colors.white.withValues(alpha: .9),
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
12.sizeBoxH,
0.5.line,
12.sizeBoxH,
Row(
children: [
Text(
"更新版本 v ${widget.updateInfo.verName}",
style: TextStyle(
color: Colors.white.withValues(alpha: .9),
fontSize: 12,
),
),
],
),
12.sizeBoxH,
SizedBox(
height: 175,
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Row(
children: [
//Row 不给约束,Text 会按自然宽度排版撑出去,必须 Expanded 收回可用宽度才会换行
Expanded(
child: Text(
widget.updateInfo.description
?.replaceAll("|", "\n") ??
"",
style: TextStyle(
color: Colors.white.withValues(alpha: .55),
fontSize: 12,
height: 1.6,
),
),
),
],
),
),
),
],
),
18.sizeBoxH,
InkWell(
enableFeedback: false,
onTap: () => Get.to(() => const MineAccountIdentityPage()),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"保存账号凭证",
style: TextStyle(
color: Colors.white.withValues(alpha: .55),
fontSize: 14,
),
),
3.sizeBoxW,
Icon(
Icons.arrow_forward_ios_outlined,
size: 14,
color: Color(0xffDCDCDC),
)
],
),
),
18.sizeBoxH,
if (isDownloading) ...[
Container(
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(3),
child: LinearProgressIndicator(
value: progress,
minHeight: 3,
backgroundColor: const Color(0xffBEBEBE),
valueColor: AlwaysStoppedAnimation(AppColors.actionRed),
),
),
12.sizeBoxH,
Text(
"${((progress * 100).toInt())}%",
style: TextStyle(
fontSize: 16,
color: Colors.white.withValues(alpha: .55),
),
),
],
),
),
] else
_buildButtonMenu(),
],
),
);
}
Widget _buildButtonMenu() {
if (widget.updateInfo.forcedUpdate == true) {
return _buildUpdateBtn();
} else {
return Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => Get.back(),
child: Container(
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: Colors.white.withValues(alpha: .1),
),
child: Text(
"暂不升级",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xff989898),
),
),
),
),
),
const SizedBox(width: 12),
Expanded(child: _buildUpdateBtn()),
],
);
}
}
Widget _buildUpdateBtn() {
return GestureDetector(
onTap: _onUpdate,
child: Container(
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.actionRed,
borderRadius: BorderRadius.circular(3),
),
child: const Text(
"立即升级",
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
);
}
///下载 apk(仅 AndroidiOS 走商店)
Future<void> _downloadApk(String url) async {
final saveDirectory =
path.join((await getExternalStorageDirectory())?.path ?? "", "apk");
DioSliceRetryDownloader(DioCli(), url, null, saveDirectory, "paofu.apk",
onReceiveProgress: (count, total) {
progress = (count / total);
setState(() {});
}, onRetry: (int retryCount) {
showToast("下载发生错误正在自动重试中...($retryCount)");
}).download().then((String savePath) async {
apkPath = savePath;
await InstallUtil.installApk(apkPath);
}).catchError((e) {
showToast("下载发生错误,点击重新进行下载.");
setState(() => isDownloading = false);
});
}
}