import 'dart:async'; import 'dart:io'; // 添加新的导入 import 'package:dio/dio.dart'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:get_storage/get_storage.dart'; import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; import 'package:vbvs_app/component/tool/FrostedDialog.dart'; import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; import 'package:vbvs_app/controller/message/message_controller.dart'; import 'package:vbvs_app/controller/setting/language/language_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/enum/APPPackageType.dart'; import 'package:vbvs_app/enum/LoginStatus.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/main_bottom/e_page.dart'; import 'package:vbvs_app/pages/main_bottom/home_page.dart'; import 'package:vbvs_app/pages/main_bottom/message_page.dart'; import 'package:vbvs_app/pages/main_bottom/mine_page.dart'; // 创建一个专门管理下载状态的Controller class DownloadController extends GetxController { RxDouble downloadProgress = 0.0.obs; RxBool isDownloading = false.obs; RxString downloadStatus = '等待下载'.obs; CancelToken? cancelToken; String? downloadFilePath; void startDownload() { isDownloading.value = true; downloadProgress.value = 0.0; downloadStatus.value = '开始下载'; } void updateProgress(double progress) { downloadProgress.value = progress; downloadStatus.value = '下载中: ${(progress * 100).toStringAsFixed(1)}%'; } void completeDownload(String filePath) { isDownloading.value = false; downloadProgress.value = 1.0; downloadStatus.value = '下载完成'; downloadFilePath = filePath; } void cancelDownload() { if (cancelToken != null) { cancelToken!.cancel(); cancelToken = null; } isDownloading.value = false; downloadProgress.value = 0.0; downloadStatus.value = '下载已取消'; } void errorDownload(String error) { isDownloading.value = false; downloadStatus.value = '下载失败: $error'; } } class MainPageBottomChange extends GetView { GlobalController globalController = Get.find(); ThemeController themeController = Get.find(); MessageController messageController = Get.find(); final getStorage = GetStorage(); late final List arr; late final List bottomItems; DateTime? _lastBackPressedTime; LanguageController languageController = Get.find(); bool select = false; // 是否已经提示过升级弹窗 // 使用GetX Controller来管理下载状态 final DownloadController downloadController = Get.put(DownloadController()); MainPageBottomChange({super.key}) { // ✅ 根据是否测试账号动态生成页面 if (!AppConstants.is_test_account) { arr = [ HomePage(), EPage(sleepUri: "https://xiaoe.he-info.cn/"), MessagePage(), MinePage(), ]; bottomItems = [ getBottomNavigationBarItem("assets/img/menu/home.svg", "assets/img/menu/n_home.svg", "菜单.首页".tr), getBottomNavigationBarItem( "assets/img/menu/e.svg", "assets/img/menu/n_e.svg", "菜单.小e".tr), getBottomNavigationBarItem("assets/img/menu/message.svg", "assets/img/menu/n_message.svg", "菜单.消息".tr, showBadge: true), getBottomNavigationBarItem("assets/img/menu/mine.svg", "assets/img/menu/n_mine.svg", "菜单.我的".tr), ]; } else { arr = [ HomePage(), MessagePage(), MinePage(), ]; bottomItems = [ getBottomNavigationBarItem("assets/img/menu/home.svg", "assets/img/menu/n_home.svg", "菜单.首页".tr), getBottomNavigationBarItem("assets/img/menu/message.svg", "assets/img/menu/n_message.svg", "菜单.消息".tr, showBadge: (messageController.model.body_message_read == 1 || messageController.model.system_message_read == 1)), getBottomNavigationBarItem("assets/img/menu/mine.svg", "assets/img/menu/n_mine.svg", "菜单.我的".tr), ]; } } // iOS跳转AppStore方法 Future _openAppStore() async { const appStoreId = '6746246837'; // 你的 App ID // 优先尝试使用 App Store 应用打开 final directUrl = Uri.parse('itms-apps://itunes.apple.com/app/id$appStoreId'); final webUrl = Uri.parse('https://apps.apple.com/app/id$appStoreId'); try { // 使用新版本的 canLaunchUrl 和 launchUrl if (await canLaunchUrl(directUrl)) { await launchUrl( directUrl, mode: LaunchMode.externalApplication, // 重要:使用外部应用打开 ); } else if (await canLaunchUrl(webUrl)) { // 备用:使用网页版 await launchUrl( webUrl, mode: LaunchMode.externalApplication, ); } else { // 如果都无法打开 NewTopSlideNotification.show( text: "无法打开AppStore链接".tr, textColor: themeController.currentColor.sc9, ); } } catch (e) { print('打开 App Store 失败: $e'); NewTopSlideNotification.show( text: "打开AppStore失败".tr, textColor: themeController.currentColor.sc9, ); } } // 检查并请求存储权限(Android) Future _requestStoragePermission() async { return true; if (!Platform.isAndroid) return true; var status = await Permission.storage.status; if (!status.isGranted) { status = await Permission.storage.request(); } if (Platform.isAndroid && status.isGranted) { // Android 11及以上需要请求管理外部存储权限 if (await Permission.manageExternalStorage.isRestricted) { status = await Permission.manageExternalStorage.request(); } } return status.isGranted; } // 下载APK文件 Future _downloadApk({ required String url, required String version, }) async { try { // 请求存储权限 bool hasPermission = await _requestStoragePermission(); if (!hasPermission) { downloadController.errorDownload("需要存储权限才能下载应用更新"); NewTopSlideNotification.show( text: "需要存储权限才能下载应用更新".tr, textColor: themeController.currentColor.sc9); return; } // 创建下载目录 Directory? externalDir = await getExternalStorageDirectory(); String downloadDir = '${externalDir!.path}/Download'; // 确保目录存在 await Directory(downloadDir).create(recursive: true); // 生成文件名 String fileName = 'app_update_$version.apk'; String filePath = '$downloadDir/$fileName'; // 如果文件已存在,先删除 File existingFile = File(filePath); if (await existingFile.exists()) { await existingFile.delete(); } // 创建Dio实例 Dio dio = Dio(); downloadController.cancelToken = CancelToken(); downloadController.startDownload(); // 开始下载 try { await dio.download( url, filePath, cancelToken: downloadController.cancelToken, onReceiveProgress: (received, total) { if (total != -1) { double progress = received / total; downloadController.updateProgress(progress); } }, options: Options( headers: { 'User-Agent': 'Dart/2.18 (dart:io)', }, receiveTimeout: const Duration(minutes: 5), ), ); } catch (e) { ef.log("msg"); } downloadController.completeDownload(filePath); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) { downloadController.errorDownload("下载已取消"); } else { downloadController.errorDownload("下载失败: ${e.toString()}"); } NewTopSlideNotification.show( text: "服务器错误,请稍后再试".tr, textColor: themeController.currentColor.sc9); } finally { downloadController.cancelToken = null; } } // 安装APK文件(Android) Future _installApk(String filePath) async { if (!Platform.isAndroid) return; try { // Android 8.0+ 需要「允许安装未知来源应用」权限 var status = await Permission.requestInstallPackages.status; if (!status.isGranted) { status = await Permission.requestInstallPackages.request(); if (!status.isGranted) { Get.snackbar("权限被拒绝", "需要安装应用的权限"); return; } } // 使用 open_file 打开 APK(系统安装器) final result = await OpenFile.open( filePath, type: "application/vnd.android.package-archive", ); if (result.type == ResultType.done) { // Get.snackbar("提示", "正在打开安装程序..."); } else { Get.defaultDialog( title: "安装提示".tr, middleText: "无法自动安装,请手动安装\n路径:\n$filePath".tr, textConfirm: "确定".tr, onConfirm: () => Get.back(), ); } } catch (e) { // Get.snackbar("安装失败", "错误: $e\n请手动安装: $filePath"); // SystemNavigator.pop(); } } // 显示更新对话框 void _showUpdateDialog( BuildContext context, String newVersion, String updateContent, String updateUrl, ) { select = true; // 使用Obx来包装对话框内容 Get.dialog( WillPopScope( onWillPop: () async => false, // 禁止返回键关闭 child: FrostedDialog( blurSigma: 3.0, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8.0), ), padding: EdgeInsetsDirectional.fromSTEB(64.rpx, 0, 64.rpx, 0), child: Container( width: double.infinity, constraints: BoxConstraints( maxHeight: MediaQuery.sizeOf(context).height * 0.656, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 使用Obx监听下载状态 Obx(() { return Align( alignment: AlignmentDirectional(0, 0), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 0.rpx, 93.rpx, 0, 74.rpx), child: Column( children: [ /// 上半部分:居中对齐 Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: 94.rpx, height: 70.rpx, child: SvgPicture.asset( 'assets/img/icon/upgrade.svg', fit: BoxFit.cover, ), ), SizedBox(height: 37.rpx), Text( downloadController.isDownloading.value ? "正在下载更新...".tr : "检测到新版本!".tr, style: TextStyle( color: stringToColor("#333333"), fontSize: AppConstants().title_text_fontSize, ), ), ], ), SizedBox(height: 37.rpx), /// 显示下载进度 if (downloadController.isDownloading.value) Column( children: [ LinearProgressIndicator( value: downloadController .downloadProgress.value, backgroundColor: Colors.grey[200], valueColor: AlwaysStoppedAnimation( Colors.blue), ), SizedBox(height: 10), Text( downloadController.downloadStatus.value, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), SizedBox(height: 20), ], ), /// 更新信息 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("当前版本号".tr + ":" + AppConstants.theh_app_version), Text("升级后版本".tr + ":" + newVersion), SizedBox(height: 10), Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), ), child: Text( "升级内容".tr + ":" + updateContent, style: TextStyle( fontSize: 14, color: Colors.grey[700], ), ), ), ], ), ], ), ), ); }), // 按钮区域 Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx), child: Obx(() { return Column( children: [ // 主要按钮(升级/下载按钮) CustomCard( gradientDirection: GradientDirection.vertical, borderRadius: AppConstants().button_container_radius, onTap: () async { if (Platform.isIOS) { // 跳转AppStore await _openAppStore(); Get.back(); } else if (!downloadController .isDownloading.value) { // 开始下载 _downloadApk( url: updateUrl, version: newVersion, ); } }, colors: [ themeController.currentColor.sc1, themeController.currentColor.sc2, ], child: Container( width: MediaQuery.sizeOf(context).width, height: MediaQuery.sizeOf(context).height * 0.055, constraints: BoxConstraints( minWidth: 500.rpx, minHeight: 90.rpx, ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( downloadController.isDownloading.value ? "下载中...".tr : Platform.isIOS ? "前往AppStore".tr : "升级".tr, style: TextStyle( color: Colors.white, fontFamily: 'Inter', fontSize: AppConstants().normal_text_fontSize, letterSpacing: 0.0, ), ), ].divide(SizedBox(width: 17.rpx)), ), ), ), ], ); }), ), ], ), ), ), ), ), ); // 监听下载完成 ever(downloadController.isDownloading, (isDownloading) async { if (!isDownloading && downloadController.downloadProgress.value >= 1.0) { // 下载完成,关闭对话框并安装 Get.back(); // Get.defaultDialog( // title: "下载完成".tr, // middleText: "新版本已下载完成,是否立即安装?".tr, // textConfirm: "安装".tr, // textCancel: "稍后".tr, // onConfirm: () { // Get.back(); // _installApk(downloadController.downloadFilePath!); // }, // onCancel: () { // Get.back(); // Get.snackbar( // "提示", "安装文件保存在: ${downloadController.downloadFilePath}"); // }, // ); await Future.delayed(Duration(milliseconds: 2000)); _installApk(downloadController.downloadFilePath!); } }); } getBottomNavigationBarItem(String svgPath, String actSvgPath, String label, {double size = 0, bool isEmpty = false, bool showBadge = false}) { if (size == 0) size = 36.rpx; Widget buildIcon(String path) { return Obx(() { int type1 = messageController.model.body_message_read!; int type2 = messageController.model.system_message_read!; return Padding( padding: EdgeInsets.only(bottom: 6.rpx), child: isEmpty ? Container() : Stack( clipBehavior: Clip.none, children: [ SvgPicture.asset( path, width: size, height: size, ), if ((type1 == 1 || type2 == 1) && showBadge) Positioned( right: -20.rpx, top: -2.rpx, child: Container( width: 14.rpx, height: 14.rpx, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), ), ), ], ), ); }); } return BottomNavigationBarItem( icon: buildIcon(actSvgPath), activeIcon: buildIcon(svgPath), label: label, ); } @override Widget build(BuildContext context) { if (AppConstants().ent_type != APPPackageType.MHT.code && isShowPrivacyOriginFromTHAPP()) { Future.delayed(const Duration(milliseconds: 0), () { String? isShowYingShiDialog = getStorage.read("isShowYingShiDialog"); if (isShowYingShiDialog == null || isShowYingShiDialog != "true") { String btnName = "同意".tr; String cancelName = "取消".tr; if (Platform.isAndroid) cancelName = "退出".tr; showCustomConfirmOfWebViewDialog(context, "隐私协议".tr, getPrivacy(1), btnName: btnName, showCancel: true, cancelName: cancelName) .then((e) async { if (e == "confirm") { getStorage.write("isShowYingShiDialog", "true"); await Future.delayed(const Duration(milliseconds: 1000)); if (select != true) { // 判断APP是否需要更新 UserInfoController userInfoController = Get.find(); userInfoController .checkAPPUpdate(AppConstants.theh_app_version) .then((data) { if (data != null) { if (data['update'] != null && data['update'] == true) { // 需要更新 String updateUrl = data['file']['path']; String currentVersion = AppConstants.theh_app_version; String newVersion = data['file']['version'] ?? "1.2.0"; String updateContent = data['file']['desc'] ?? "修复已知bug".tr; try { _showUpdateDialog( context, newVersion, updateContent, updateUrl); } catch (e) { ef.log("更新对话框显示失败: $e"); } } } }); } } else { if (cancelName == "退出") SystemNavigator.pop(); } }); } else { if (select != true) { // 判断APP是否需要更新 UserInfoController userInfoController = Get.find(); userInfoController .checkAPPUpdate(AppConstants.theh_app_version) .then((data) { if (data != null) { if (data['update'] != null && data['update'] == true) { // 需要更新 String updateUrl = data['file']['path']; String currentVersion = AppConstants.theh_app_version; String newVersion = data['file']['version'] ?? "1.2.0"; String updateContent = data['file']['desc'] ?? "修复已知bug".tr; try { _showUpdateDialog( context, newVersion, updateContent, updateUrl); } catch (e) { ef.log("更新对话框显示失败: $e"); } } } }); } } }); } return Obx(() { final currentLanguage = languageController.selectLanguage.value; // 监听此变量变化 return PopScope( canPop: false, onPopInvokedWithResult: (disposition, result) async { UserInfoController userInfoController = Get.find(); if (userInfoController.model.isProgrammaticPop) { userInfoController.model.isProgrammaticPop = false; return; } if (Platform.isAndroid) { var flag = await _handleBackPressed(context); if (flag) { SystemNavigator.pop(); } } }, child: Obx(() { int currentIndex = controller.model.currentIndex; // ✅ 防止 index 超出范围(例如测试账号少一个 tab) if (currentIndex >= arr.length) { currentIndex = 0; controller.model.currentIndex = 0; } if (globalController.model.hideBottomNavigationBar == true) { return Scaffold( body: IndexedStack( index: currentIndex, children: arr.map((page) => SizedBox.expand(child: page)).toList(), ), ); } else { return Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage(getBackgroundImage()), fit: BoxFit.fill, ), ), child: Scaffold( backgroundColor: Colors.transparent, body: IndexedStack( index: currentIndex, children: arr.map((page) => SizedBox.expand(child: page)).toList(), ), bottomNavigationBar: Theme( data: Theme.of(context).copyWith( splashFactory: NoSplash.splashFactory, splashColor: Colors.transparent, highlightColor: Colors.transparent, ), child: Container( decoration: BoxDecoration( border: Border( top: BorderSide( color: themeController.currentColor.sc4.withOpacity(0.5), width: AppConstants().border_width, ), ), ), child: BottomNavigationBar( unselectedItemColor: themeController.currentColor.sc4, selectedItemColor: themeController.currentColor.sc1, backgroundColor: themeController.currentColor.sc5, selectedFontSize: 26.rpx, unselectedFontSize: 26.rpx, type: BottomNavigationBarType.fixed, currentIndex: currentIndex, onTap: (index) { Future.delayed(const Duration(milliseconds: 100), () { UserInfoController userInfoController = Get.find(); bool isLoggedIn = userInfoController.model.login == LoginStatus.LOGIN.code; // ✅ 仅非测试账号检查登录状态 if (!AppConstants.is_test_account && (index == 2) && !isLoggedIn) { TopSlideNotification.show( context, text: "必须登录提示".tr, textColor: themeController.currentColor.sc9, ); Future.delayed(const Duration(milliseconds: 50), () { if (Get.currentRoute == '/ePage' || Get.currentRoute == '/messagePage') { Get.back(); } Future.delayed(const Duration(milliseconds: 100), () { Get.toNamed("/otherLoginPage"); }); }); return; } if (controller.model.currentIndex != index) { globalController.model.hideBottomNavigationBar = false; globalController.updateAll(); } controller.model.currentIndex = index; controller.updateAll(); }); }, items: bottomItems, ), ), ), ), ); } }), ); }); } Future _handleBackPressed(BuildContext context) async { final currentTime = DateTime.now(); if (_lastBackPressedTime == null || currentTime.difference(_lastBackPressedTime!) > const Duration(seconds: 2)) { _lastBackPressedTime = currentTime; TopSlideNotification.show(context, text: "滑动退出提醒".tr); return false; } else { return true; } } //太和e护衍生app是否显示协议 bool isShowPrivacyOriginFromTHAPP() { if (AppConstants().ent_type == APPPackageType.TH.code) { return true; } if (AppConstants().ent_type == APPPackageType.DONGHUA.code) { return true; } return false; } }