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):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ReachabilitySwift
|
- 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):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- EasyPermissionX/Camera (0.0.2)
|
- EasyPermissionX/Camera (0.0.2)
|
||||||
@ -35,12 +41,10 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- webview_flutter_wkwebview (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- 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`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- EasyPermissionX/Camera
|
- EasyPermissionX/Camera
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
@ -53,10 +57,10 @@ DEPENDENCIES:
|
|||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
|
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
|
||||||
|
- Crisp
|
||||||
- EasyPermissionX
|
- EasyPermissionX
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
@ -65,6 +69,8 @@ SPEC REPOS:
|
|||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
|
crisp_chat:
|
||||||
|
:path: ".symlinks/plugins/crisp_chat/ios"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
@ -87,11 +93,11 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
webview_flutter_wkwebview:
|
|
||||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||||
|
Crisp: 6747c96b2b2c2a81babf1eaecd1688a65d98edd4
|
||||||
|
crisp_chat: 30994104495de23443af8d5b2c041a6df1e8464d
|
||||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||||
EasyPermissionX: ff4c438f6ee80488f873b4cb921e32d982523067
|
EasyPermissionX: ff4c438f6ee80488f873b4cb921e32d982523067
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
@ -107,7 +113,6 @@ SPEC CHECKSUMS:
|
|||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
webview_flutter_wkwebview: a4af96a051138e28e29f60101d094683b9f82188
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: 579a354deb8d6fdc55c12799569018594328642e
|
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_help_entrance.dart';
|
||||||
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
|
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
|
||||||
import 'package:easy_refresh/easy_refresh.dart';
|
import 'package:easy_refresh/easy_refresh.dart';
|
||||||
class HINodePageView extends GetView<HINodeListController> {
|
class HINodePageView extends StatefulWidget {
|
||||||
const HINodePageView({super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return HIBaseScaffold(
|
return HIBaseScaffold(
|
||||||
@ -69,6 +78,7 @@ class HINodePageView extends GetView<HINodeListController> {
|
|||||||
// 2. 关键:移除多余的 SizedBox,让 Expanded 正确工作
|
// 2. 关键:移除多余的 SizedBox,让 Expanded 正确工作
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
key: _modeSwitcherKey,
|
||||||
padding: EdgeInsets.all(4.w),
|
padding: EdgeInsets.all(4.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@ -99,7 +109,19 @@ class HINodePageView extends GetView<HINodeListController> {
|
|||||||
SizedBox(width: 12.w),
|
SizedBox(width: 12.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
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(
|
HIDialog.show(
|
||||||
|
targetOffset: targetOffset,
|
||||||
customMessageWidget: Padding(
|
customMessageWidget: Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 0.w, vertical: 0.w),
|
padding: EdgeInsets.symmetric(horizontal: 0.w, vertical: 0.w),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@ -46,6 +46,8 @@ class KRHomeView extends StatefulWidget implements HasSwipeConfig {
|
|||||||
class _KRHomeViewState extends State<KRHomeView> {
|
class _KRHomeViewState extends State<KRHomeView> {
|
||||||
late KRHomeController controller;
|
late KRHomeController controller;
|
||||||
|
|
||||||
|
final GlobalKey _quickConnectTextKey = GlobalKey();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -198,12 +200,15 @@ class _KRHomeViewState extends State<KRHomeView> {
|
|||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
// 2. “闪连”标签
|
// 2. “闪连”标签
|
||||||
Text(
|
Container(
|
||||||
'闪连',
|
key: _quickConnectTextKey,
|
||||||
style: TextStyle(
|
child: Text(
|
||||||
color: Colors.white,
|
'闪连',
|
||||||
fontSize: 20,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
color: Colors.white,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -214,10 +219,26 @@ class _KRHomeViewState extends State<KRHomeView> {
|
|||||||
SizedBox(width: 6), // 文字和问号图标之间的间距
|
SizedBox(width: 6), // 文字和问号图标之间的间距
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
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(
|
HIDialog.show(
|
||||||
title: '闪连功能',
|
title: '闪连功能',
|
||||||
message:
|
message:
|
||||||
'开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
|
'开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
|
||||||
|
targetOffset: targetOffset,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: KrLocalImage(
|
child: KrLocalImage(
|
||||||
|
|||||||
@ -32,7 +32,9 @@ class KRInviteView extends GetView<KRInviteController> {
|
|||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: isKeyboardVisible
|
||||||
|
? const ClampingScrollPhysics()
|
||||||
|
: const NeverScrollableScrollPhysics(),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
@ -143,6 +145,7 @@ class KRInviteView extends GetView<KRInviteController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 40.w),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
// 🟢 第三部分:接受他人邀请
|
// 🟢 第三部分:接受他人邀请
|
||||||
@ -166,6 +169,8 @@ class KRInviteView extends GetView<KRInviteController> {
|
|||||||
RepaintBoundary(
|
RepaintBoundary(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: controller.otherInviteCodeController,
|
controller: controller.otherInviteCodeController,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
onSubmitted: (_) => controller.kr_handleBindInviteCode(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -190,33 +195,36 @@ class KRInviteView extends GetView<KRInviteController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.w),
|
if (!isKeyboardVisible) ...[
|
||||||
SizedBox(
|
SizedBox(height: 10.w),
|
||||||
width: double.infinity,
|
SizedBox(
|
||||||
height: 50.w,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
height: 50.w,
|
||||||
style: ElevatedButton.styleFrom(
|
child: ElevatedButton(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
style: ElevatedButton.styleFrom(
|
||||||
shape: RoundedRectangleBorder(
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
borderRadius: BorderRadius.circular(1000.r),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(1000.r),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
onPressed: () =>
|
||||||
onPressed: () => controller.kr_handleBindInviteCode(),
|
controller.kr_handleBindInviteCode(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'保存',
|
'保存',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 16.sp,
|
fontSize: 16.sp,
|
||||||
fontWeight: FontWeight.bold,
|
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 const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Text(
|
return DefaultTextStyle(
|
||||||
'购买套餐',
|
style: const TextStyle(
|
||||||
style: TextStyle(
|
decoration: TextDecoration.none, // 核心:显式声明无装饰
|
||||||
color: Colors.black,
|
),
|
||||||
fontSize: 12.sp,
|
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 时自动启用)
|
/// 是否显示按钮 loading(autoClose=false 时自动启用)
|
||||||
final bool? showLoading;
|
final bool? showLoading;
|
||||||
|
final Offset? targetOffset;
|
||||||
|
|
||||||
|
static double get kDialogWidth => 245.w < 280.0 ? 280.0 : 245.w;
|
||||||
|
|
||||||
const HIDialog({
|
const HIDialog({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -38,6 +41,7 @@ class HIDialog extends StatefulWidget {
|
|||||||
this.preventBackDismiss = false,
|
this.preventBackDismiss = false,
|
||||||
this.autoClose = true,
|
this.autoClose = true,
|
||||||
this.showLoading,
|
this.showLoading,
|
||||||
|
this.targetOffset,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
static Future<void> show({
|
static Future<void> show({
|
||||||
@ -52,6 +56,7 @@ class HIDialog extends StatefulWidget {
|
|||||||
bool preventBackDismiss = false,
|
bool preventBackDismiss = false,
|
||||||
bool autoClose = true,
|
bool autoClose = true,
|
||||||
bool? showLoading,
|
bool? showLoading,
|
||||||
|
Offset? targetOffset,
|
||||||
}) {
|
}) {
|
||||||
return Get.dialog(
|
return Get.dialog(
|
||||||
HIDialog(
|
HIDialog(
|
||||||
@ -66,9 +71,11 @@ class HIDialog extends StatefulWidget {
|
|||||||
preventBackDismiss: preventBackDismiss,
|
preventBackDismiss: preventBackDismiss,
|
||||||
autoClose: autoClose,
|
autoClose: autoClose,
|
||||||
showLoading: showLoading,
|
showLoading: showLoading,
|
||||||
|
targetOffset: targetOffset,
|
||||||
),
|
),
|
||||||
barrierDismissible: barrierDismissible,
|
barrierDismissible: barrierDismissible,
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
|
useSafeArea: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,20 +119,127 @@ class _HIDialogState extends State<HIDialog> {
|
|||||||
),
|
),
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
height: 20.w,
|
height: 20.w,
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
child: const CircularProgressIndicator(
|
child: const CircularProgressIndicator(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
widget.confirmText ?? (widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
|
widget.confirmText ??
|
||||||
style: TextStyle(
|
(widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
|
||||||
fontSize: 16.sp,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontSize: 16.sp,
|
||||||
color: Colors.black,
|
fontWeight: FontWeight.w600,
|
||||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
final dialog = Dialog(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
insetPadding: EdgeInsets.all(20.w),
|
insetPadding: EdgeInsets.all(20.w),
|
||||||
child: Transform.translate(
|
child: Transform.translate(
|
||||||
offset: Offset(0, -30.w),
|
offset: Offset(0, -30.w),
|
||||||
child: ClipRRect(
|
child: _buildDialogContent(),
|
||||||
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',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -572,9 +572,10 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||||
@ -585,6 +586,7 @@
|
|||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
@ -705,9 +707,10 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||||
@ -718,6 +721,7 @@
|
|||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "HiFastVPN-Mac-Pord";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
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 ""
|
||||||