feat: 修改hidialog弹窗位置,增加macos dmg打包配置

This commit is contained in:
speakeloudest 2026-01-12 00:45:52 -08:00
parent 2d1f391932
commit e6ee3d6db8
22 changed files with 393 additions and 157 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

View File

@ -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

View File

@ -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(

View File

@ -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,7 +200,9 @@ class _KRHomeViewState extends State<KRHomeView> {
),
SizedBox(width: 8),
// 2.
Text(
Container(
key: _quickConnectTextKey,
child: Text(
'闪连',
style: TextStyle(
color: Colors.white,
@ -206,6 +210,7 @@ class _KRHomeViewState extends State<KRHomeView> {
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(

View File

@ -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,6 +195,7 @@ class KRInviteView extends GetView<KRInviteController> {
),
),
),
if (!isKeyboardVisible) ...[
SizedBox(height: 10.w),
SizedBox(
width: double.infinity,
@ -201,7 +207,8 @@ class KRInviteView extends GetView<KRInviteController> {
borderRadius: BorderRadius.circular(1000.r),
),
),
onPressed: () => controller.kr_handleBindInviteCode(),
onPressed: () =>
controller.kr_handleBindInviteCode(),
child: Text(
'保存',
style: TextStyle(
@ -213,10 +220,11 @@ class KRInviteView extends GetView<KRInviteController> {
),
),
],
],
);
}),
//
SizedBox(height: 90.w),
SizedBox(height: isKeyboardVisible ? 20.w : 90.w),
],
),
),

View File

@ -124,11 +124,18 @@ class GlobalOverlayService extends GetxService {
return const SizedBox.shrink();
}
return Text(
return DefaultTextStyle(
style: const TextStyle(
decoration: TextDecoration.none, //
),
child: Text(
'购买套餐',
style: TextStyle(
color: Colors.black,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none, //
),
),
);
}),

View File

@ -24,6 +24,9 @@ class HIDialog extends StatefulWidget {
/// loadingautoClose=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,
);
}
@ -120,7 +127,8 @@ class _HIDialogState extends State<HIDialog> {
),
)
: Text(
widget.confirmText ?? (widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
widget.confirmText ??
(widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
@ -131,14 +139,8 @@ class _HIDialogState extends State<HIDialog> {
);
}
@override
Widget build(BuildContext context) {
final dialog = Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.all(20.w),
child: Transform.translate(
offset: Offset(0, -30.w),
child: ClipRRect(
Widget _buildDialogContent() {
return ClipRRect(
borderRadius: BorderRadius.circular(34.r),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), //
@ -189,7 +191,9 @@ class _HIDialogState extends State<HIDialog> {
),
),
],
if (widget.confirmText != null || widget.cancelText != null || (widget.confirmText == null && widget.cancelText == null)) ...[
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(
@ -238,8 +242,37 @@ 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: _buildDialogContent(),
),
);
if (widget.preventBackDismiss) {

View File

@ -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;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 B

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 37 KiB

BIN
scripts/assets/dmg_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 KiB

136
scripts/build_dmg.sh Executable file
View 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 ""