feat: 修改hidialog弹窗位置,增加macos dmg打包配置
BIN
assets/images/logo-1024-1024.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
@ -2,6 +2,12 @@ PODS:
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- ReachabilitySwift
|
||||
- Crisp (2.8.2):
|
||||
- Crisp/Crisp (= 2.8.2)
|
||||
- Crisp/Crisp (2.8.2)
|
||||
- crisp_chat (2.4.1):
|
||||
- Crisp (~> 2.8.2)
|
||||
- Flutter
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- EasyPermissionX/Camera (0.0.2)
|
||||
@ -35,12 +41,10 @@ PODS:
|
||||
- Flutter
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- crisp_chat (from `.symlinks/plugins/crisp_chat/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- EasyPermissionX/Camera
|
||||
- Flutter (from `Flutter`)
|
||||
@ -53,10 +57,10 @@ DEPENDENCIES:
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
|
||||
- Crisp
|
||||
- EasyPermissionX
|
||||
- OrderedSet
|
||||
- ReachabilitySwift
|
||||
@ -65,6 +69,8 @@ SPEC REPOS:
|
||||
EXTERNAL SOURCES:
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
crisp_chat:
|
||||
:path: ".symlinks/plugins/crisp_chat/ios"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
Flutter:
|
||||
@ -87,11 +93,11 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
Crisp: 6747c96b2b2c2a81babf1eaecd1688a65d98edd4
|
||||
crisp_chat: 30994104495de23443af8d5b2c041a6df1e8464d
|
||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||
EasyPermissionX: ff4c438f6ee80488f873b4cb921e32d982523067
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
@ -107,7 +113,6 @@ SPEC CHECKSUMS:
|
||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
webview_flutter_wkwebview: a4af96a051138e28e29f60101d094683b9f82188
|
||||
|
||||
PODFILE CHECKSUM: 579a354deb8d6fdc55c12799569018594328642e
|
||||
|
||||
|
||||
@ -11,9 +11,18 @@ import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/hi_help_entrance.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
class HINodePageView extends GetView<HINodeListController> {
|
||||
class HINodePageView extends StatefulWidget {
|
||||
const HINodePageView({super.key});
|
||||
|
||||
@override
|
||||
State<HINodePageView> createState() => _HINodePageViewState();
|
||||
}
|
||||
|
||||
class _HINodePageViewState extends State<HINodePageView> {
|
||||
final GlobalKey _modeSwitcherKey = GlobalKey();
|
||||
|
||||
HINodeListController get controller => Get.find<HINodeListController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HIBaseScaffold(
|
||||
@ -69,6 +78,7 @@ class HINodePageView extends GetView<HINodeListController> {
|
||||
// 2. 关键:移除多余的 SizedBox,让 Expanded 正确工作
|
||||
Expanded(
|
||||
child: Container(
|
||||
key: _modeSwitcherKey,
|
||||
padding: EdgeInsets.all(4.w),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
@ -99,7 +109,19 @@ class HINodePageView extends GetView<HINodeListController> {
|
||||
SizedBox(width: 12.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Offset? targetOffset;
|
||||
final renderBox = _modeSwitcherKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
if (renderBox != null) {
|
||||
final position = renderBox.localToGlobal(Offset.zero);
|
||||
final size = renderBox.size;
|
||||
targetOffset = Offset(
|
||||
position.dx + (size.width / 2) - (HIDialog.kDialogWidth / 2),
|
||||
position.dy + size.height + 10.w,
|
||||
);
|
||||
}
|
||||
|
||||
HIDialog.show(
|
||||
targetOffset: targetOffset,
|
||||
customMessageWidget: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0.w, vertical: 0.w),
|
||||
child: Column(
|
||||
|
||||
@ -46,6 +46,8 @@ class KRHomeView extends StatefulWidget implements HasSwipeConfig {
|
||||
class _KRHomeViewState extends State<KRHomeView> {
|
||||
late KRHomeController controller;
|
||||
|
||||
final GlobalKey _quickConnectTextKey = GlobalKey();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -198,12 +200,15 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
// 2. “闪连”标签
|
||||
Text(
|
||||
'闪连',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
Container(
|
||||
key: _quickConnectTextKey,
|
||||
child: Text(
|
||||
'闪连',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -214,10 +219,26 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
SizedBox(width: 6), // 文字和问号图标之间的间距
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Offset? targetOffset;
|
||||
final renderBox = _quickConnectTextKey
|
||||
.currentContext
|
||||
?.findRenderObject() as RenderBox?;
|
||||
if (renderBox != null) {
|
||||
final position =
|
||||
renderBox.localToGlobal(Offset.zero);
|
||||
targetOffset = Offset(
|
||||
position.dx - 26,
|
||||
position.dy +
|
||||
renderBox.size.height +
|
||||
10.w,
|
||||
);
|
||||
}
|
||||
|
||||
HIDialog.show(
|
||||
title: '闪连功能',
|
||||
message:
|
||||
'开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
|
||||
targetOffset: targetOffset,
|
||||
);
|
||||
},
|
||||
child: KrLocalImage(
|
||||
|
||||
@ -32,7 +32,9 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
physics: isKeyboardVisible
|
||||
? const ClampingScrollPhysics()
|
||||
: const NeverScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
@ -143,6 +145,7 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 40.w),
|
||||
const Spacer(),
|
||||
|
||||
// 🟢 第三部分:接受他人邀请
|
||||
@ -166,6 +169,8 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
RepaintBoundary(
|
||||
child: TextField(
|
||||
controller: controller.otherInviteCodeController,
|
||||
textInputAction: TextInputAction.done,
|
||||
onSubmitted: (_) => controller.kr_handleBindInviteCode(),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
decoration: InputDecoration(
|
||||
@ -190,33 +195,36 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10.w),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50.w,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(1000.r),
|
||||
if (!isKeyboardVisible) ...[
|
||||
SizedBox(height: 10.w),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50.w,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(1000.r),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () => controller.kr_handleBindInviteCode(),
|
||||
child: Text(
|
||||
'保存',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
onPressed: () =>
|
||||
controller.kr_handleBindInviteCode(),
|
||||
child: Text(
|
||||
'保存',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}),
|
||||
// 底部留白,确保键盘弹出后能滚过遮挡区域
|
||||
SizedBox(height: 90.w),
|
||||
SizedBox(height: isKeyboardVisible ? 20.w : 90.w),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -124,11 +124,18 @@ class GlobalOverlayService extends GetxService {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Text(
|
||||
'购买套餐',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12.sp,
|
||||
return DefaultTextStyle(
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.none, // 核心:显式声明无装饰
|
||||
),
|
||||
child: Text(
|
||||
'购买套餐',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.none, // 也可以在这里直接写
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
||||
@ -24,6 +24,9 @@ class HIDialog extends StatefulWidget {
|
||||
|
||||
/// 是否显示按钮 loading(autoClose=false 时自动启用)
|
||||
final bool? showLoading;
|
||||
final Offset? targetOffset;
|
||||
|
||||
static double get kDialogWidth => 245.w < 280.0 ? 280.0 : 245.w;
|
||||
|
||||
const HIDialog({
|
||||
Key? key,
|
||||
@ -38,6 +41,7 @@ class HIDialog extends StatefulWidget {
|
||||
this.preventBackDismiss = false,
|
||||
this.autoClose = true,
|
||||
this.showLoading,
|
||||
this.targetOffset,
|
||||
}) : super(key: key);
|
||||
|
||||
static Future<void> show({
|
||||
@ -52,6 +56,7 @@ class HIDialog extends StatefulWidget {
|
||||
bool preventBackDismiss = false,
|
||||
bool autoClose = true,
|
||||
bool? showLoading,
|
||||
Offset? targetOffset,
|
||||
}) {
|
||||
return Get.dialog(
|
||||
HIDialog(
|
||||
@ -66,9 +71,11 @@ class HIDialog extends StatefulWidget {
|
||||
preventBackDismiss: preventBackDismiss,
|
||||
autoClose: autoClose,
|
||||
showLoading: showLoading,
|
||||
targetOffset: targetOffset,
|
||||
),
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierColor: Colors.transparent,
|
||||
useSafeArea: false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -112,20 +119,127 @@ class _HIDialogState extends State<HIDialog> {
|
||||
),
|
||||
child: _isLoading
|
||||
? SizedBox(
|
||||
height: 20.w,
|
||||
width: 20.w,
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.black,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
height: 20.w,
|
||||
width: 20.w,
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.black,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
widget.confirmText ?? (widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
widget.confirmText ??
|
||||
(widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogContent() {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(34.r),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), // 毛玻璃模糊
|
||||
child: Container(
|
||||
width: 245.w,
|
||||
padding: EdgeInsets.fromLTRB(30.w, 16.w, 30.w, 16.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDEDEDE), // 半透明底色
|
||||
borderRadius: BorderRadius.circular(34.r),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.2), // 高光边框
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.title != null) ...[
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
widget.title!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.w),
|
||||
],
|
||||
if (widget.message != null || widget.customMessageWidget != null) ...[
|
||||
Container(
|
||||
constraints: BoxConstraints(maxHeight: 200.h),
|
||||
child: SingleChildScrollView(
|
||||
child: widget.customMessageWidget ??
|
||||
Text(
|
||||
widget.message!,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black,
|
||||
height: 1.4,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (widget.confirmText != null ||
|
||||
widget.cancelText != null ||
|
||||
(widget.confirmText == null && widget.cancelText == null)) ...[
|
||||
SizedBox(height: 12.w),
|
||||
if (widget.confirmText == null && widget.cancelText == null)
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 85.w,
|
||||
child: _buildConfirmButton(),
|
||||
),
|
||||
)
|
||||
else
|
||||
Row(
|
||||
children: [
|
||||
if (widget.confirmText != null) Expanded(child: _buildConfirmButton()),
|
||||
if (widget.cancelText != null && widget.confirmText != null)
|
||||
SizedBox(width: 12.w),
|
||||
if (widget.cancelText != null)
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
widget.onCancel?.call();
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF00B7),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(23.r),
|
||||
),
|
||||
minimumSize: Size(85.w, 40.w),
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
child: Text(
|
||||
widget.cancelText!,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -133,112 +247,31 @@ class _HIDialogState extends State<HIDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.targetOffset != null) {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned(
|
||||
left: widget.targetOffset!.dx,
|
||||
top: widget.targetOffset!.dy,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 280.0),
|
||||
child: _buildDialogContent(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final dialog = Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
insetPadding: EdgeInsets.all(20.w),
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, -30.w),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(34.r),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), // 毛玻璃模糊
|
||||
child: Container(
|
||||
width: 245.w,
|
||||
padding: EdgeInsets.fromLTRB(30.w, 16.w, 30.w, 16.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDEDEDE), // 半透明底色
|
||||
borderRadius: BorderRadius.circular(34.r),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.2), // 高光边框
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.title != null) ...[
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
widget.title!,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.w),
|
||||
],
|
||||
if (widget.message != null || widget.customMessageWidget != null) ...[
|
||||
Container(
|
||||
constraints: BoxConstraints(maxHeight: 200.h),
|
||||
child: SingleChildScrollView(
|
||||
child: widget.customMessageWidget ??
|
||||
Text(
|
||||
widget.message!,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black,
|
||||
height: 1.4,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (widget.confirmText != null || widget.cancelText != null || (widget.confirmText == null && widget.cancelText == null)) ...[
|
||||
SizedBox(height: 12.w),
|
||||
if (widget.confirmText == null && widget.cancelText == null)
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 85.w,
|
||||
child: _buildConfirmButton(),
|
||||
),
|
||||
)
|
||||
else
|
||||
Row(
|
||||
children: [
|
||||
if (widget.confirmText != null) Expanded(child: _buildConfirmButton()),
|
||||
if (widget.cancelText != null && widget.confirmText != null)
|
||||
SizedBox(width: 12.w),
|
||||
if (widget.cancelText != null)
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
widget.onCancel?.call();
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF00B7),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(23.r),
|
||||
),
|
||||
minimumSize: Size(85.w, 40.w),
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
child: Text(
|
||||
widget.cancelText!,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _buildDialogContent(),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@ -572,9 +572,10 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||
@ -585,6 +586,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
@ -705,9 +707,10 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||
@ -718,6 +721,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 37 KiB |
BIN
scripts/assets/dmg_bg.png
Normal file
|
After Width: | Height: | Size: 820 KiB |
136
scripts/build_dmg.sh
Executable file
@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ HiFastVPN macOS DMG 打包脚本 ║
|
||||
# ║ ║
|
||||
# ║ 使用方法: ║
|
||||
# ║ 1. 确保已经运行 sign_and_notarize.sh 生成签名的 .app ║
|
||||
# ║ 2. chmod +x build_dmg.sh ║
|
||||
# ║ 3. ./build_dmg.sh ║
|
||||
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ======================== 路径配置 ========================
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
APP_PATH="${PROJECT_DIR}/build/macos/Build/Products/Release/HiFastVPN.app"
|
||||
BACKGROUND_IMAGE="${SCRIPT_DIR}/assets/dmg_bg.png"
|
||||
|
||||
# ======================== 提取版本号 ========================
|
||||
echo "╔═══════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ HiFastVPN macOS DMG 打包脚本 ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
PUBSPEC_PATH="${PROJECT_DIR}/pubspec.yaml"
|
||||
if [ ! -f "${PUBSPEC_PATH}" ]; then
|
||||
echo "❌ 错误: 找不到 pubspec.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(grep "^version:" "${PUBSPEC_PATH}" | sed 's/version: *//' | tr -d ' ')
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "❌ 错误: 无法提取版本号"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 版本号: ${VERSION}"
|
||||
echo "📁 项目目录: ${PROJECT_DIR}"
|
||||
echo "📦 应用路径: ${APP_PATH}"
|
||||
echo "🖼️ 背景图: ${BACKGROUND_IMAGE}"
|
||||
echo ""
|
||||
|
||||
# ======================== 检查依赖 ========================
|
||||
if ! command -v create-dmg >/dev/null 2>&1; then
|
||||
echo "❌ 错误: create-dmg 未安装(brew install create-dmg)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${APP_PATH}" ]; then
|
||||
echo "❌ 错误: HiFastVPN.app 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${BACKGROUND_IMAGE}" ]; then
|
||||
echo "❌ 错误: 背景图不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ======================== 校验背景图尺寸 ========================
|
||||
BG_INFO=$(sips -g pixelWidth -g pixelHeight "${BACKGROUND_IMAGE}")
|
||||
BG_W=$(echo "$BG_INFO" | awk '/pixelWidth/ {print $2}')
|
||||
BG_H=$(echo "$BG_INFO" | awk '/pixelHeight/ {print $2}')
|
||||
|
||||
if [ "$BG_W" != "1200" ] || [ "$BG_H" != "800" ]; then
|
||||
echo "❌ 背景图必须是 1200x800,当前是 ${BG_W}x${BG_H}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ======================== 输出目录 ========================
|
||||
OUTPUT_DIR="${PROJECT_DIR}/dist/${VERSION}"
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
|
||||
DMG_NAME="HiFastVPN-${VERSION}.dmg"
|
||||
DMG_PATH="${OUTPUT_DIR}/${DMG_NAME}"
|
||||
|
||||
echo "📂 输出目录: ${OUTPUT_DIR}"
|
||||
echo "💿 DMG 文件: ${DMG_NAME}"
|
||||
echo ""
|
||||
|
||||
rm -f "${DMG_PATH}"
|
||||
|
||||
# ======================== 创建 DMG ========================
|
||||
echo "💿 开始创建 DMG..."
|
||||
echo ""
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cp -R "${APP_PATH}" "${TEMP_DIR}/"
|
||||
|
||||
# ======================== Finder 坐标(左上原点,中心点对齐) ========================
|
||||
# 背景:1200 × 800
|
||||
# HiFastVPN.app 中心:左 228, 上 418, 尺寸 168×168
|
||||
APP_CENTER_X=236
|
||||
APP_CENTER_Y=432
|
||||
APP_ICON_SIZE=152
|
||||
APP_X=$((APP_CENTER_X + APP_ICON_SIZE / 2)) # 左上角坐标
|
||||
APP_Y=$((APP_CENTER_Y + APP_ICON_SIZE / 2))
|
||||
|
||||
# Applications 图标中心:左 742, 上 372, 尺寸 259×259
|
||||
DROP_CENTER_X=742
|
||||
DROP_CENTER_Y=372
|
||||
DROP_ICON_SIZE=259
|
||||
DROP_X=$((DROP_CENTER_X + DROP_ICON_SIZE / 2))
|
||||
DROP_Y=$((DROP_CENTER_Y + DROP_ICON_SIZE / 2))
|
||||
|
||||
# ======================== 打包 ========================
|
||||
create-dmg \
|
||||
--volname "HiFastVPN Installation" \
|
||||
--background "${BACKGROUND_IMAGE}" \
|
||||
--window-size 1200 800 \
|
||||
--icon-size "${APP_ICON_SIZE}" \
|
||||
--icon "HiFastVPN.app" "${APP_X}" "${APP_Y}" \
|
||||
--app-drop-link "${DROP_X}" "${DROP_Y}" \
|
||||
--hide-extension "HiFastVPN.app" \
|
||||
--no-internet-enable \
|
||||
"${DMG_PATH}" \
|
||||
"${TEMP_DIR}"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "${TEMP_DIR}"
|
||||
|
||||
# ======================== 完成 ========================
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ 🎉 DMG 创建完成! ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "输出文件:"
|
||||
echo " 💿 ${DMG_PATH}"
|
||||
echo ""
|
||||
echo "📝 注意事项:"
|
||||
echo " • DMG 已保存到 dist/${VERSION}/ 目录"
|
||||
echo " • 图标和 Applications 已按设计稿中心点对齐"
|
||||
echo " • 此 DMG 包含已签名和公证的 .app"
|
||||
echo " • 可以直接分发给用户使用"
|
||||
echo ""
|
||||