Просмотр исходного кода

[feat]增加引导界面,增加支付功能

Destiny 6 месяцев назад
Родитель
Сommit
09cc686794
51 измененных файлов с 914 добавлено и 227 удалено
  1. 0 4
      ios/AiKeyboard/Info.plist
  2. 68 9
      ios/AiKeyboard/KeyboardViewController.swift
  3. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/Contents.json
  4. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/keyboard_guide_button@2x.png
  5. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/keyboard_guide_button@3x.png
  6. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/Contents.json
  7. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/keyboard_guide_image@2x.png
  8. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/keyboard_guide_image@3x.png
  9. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/Contents.json
  10. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/keyboard_vip_unlock_text@2x.png
  11. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/keyboard_vip_unlock_text@3x.png
  12. 1 0
      ios/AiKeyboard/View/Cell/KeyboardCharacterCell.swift
  13. 14 4
      ios/AiKeyboard/View/MainView/KeyboardBaseView.swift
  14. 24 12
      ios/AiKeyboard/View/MainView/KeyboardHelpView.swift
  15. 4 0
      ios/AiKeyboard/View/MainView/KeyboardPrologueView.swift
  16. 31 19
      ios/AiKeyboard/View/MainView/KeyboardTeachView.swift
  17. 77 0
      ios/AiKeyboard/View/PopView/KeyboardGuideView.swift
  18. 9 2
      ios/AiKeyboard/View/PopView/KeyboardVipTipView.swift
  19. 9 0
      ios/KeyboardSharedDataManager.swift
  20. 6 0
      ios/Podfile.lock
  21. 4 6
      ios/Runner.xcodeproj/project.pbxproj
  22. 1 1
      ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  23. 80 17
      ios/Runner/AppDelegate.swift
  24. 36 1
      ios/Runner/FlutterMethodChannelManager.swift
  25. 6 0
      ios/Runner/Info.plist
  26. BIN
      ios/tbmct.ttf
  27. 2 1
      lib/data/api/request/subscribe_resume_request.dart
  28. 3 1
      lib/data/api/request/subscribe_resume_request.g.dart
  29. 4 0
      lib/data/bean/goods_info.dart
  30. 2 0
      lib/data/bean/goods_info.g.dart
  31. 3 1
      lib/data/consts/payment_type.dart
  32. 5 0
      lib/data/consts/web_url.dart
  33. 1 0
      lib/data/repository/store_repository.dart
  34. 42 42
      lib/di/get_it.config.dart
  35. 1 1
      lib/handler/wechat_login_service.dart
  36. 2 0
      lib/main.dart
  37. 11 2
      lib/module/keyboard_guide/keyboard_guide_controller.dart
  38. 19 4
      lib/module/keyboard_guide/keyboard_guide_page.dart
  39. 91 25
      lib/module/store/new_discount/new_discount_controller.dart
  40. 47 14
      lib/module/store/new_discount/new_discount_page.dart
  41. 79 20
      lib/module/store/store_controller.dart
  42. 68 32
      lib/module/store/store_page.dart
  43. 2 0
      lib/resource/string.gen.dart
  44. 13 4
      lib/utils/default_keyboard_helper.dart
  45. 6 3
      lib/utils/floating_window_helper.dart
  46. 30 0
      lib/utils/keyboard_detect_ios_util.dart
  47. 5 1
      lib/utils/keyboard_tutorial_util.dart
  48. 36 0
      lib/utils/method_chanel_ios_util.dart
  49. 4 0
      lib/utils/payment_status_manager.dart
  50. 1 1
      lib/utils/url_launcher_util.dart
  51. 1 0
      plugins/flutter_umeng/ios/Classes/FlutterUmengPlugin.swift

+ 0 - 4
ios/AiKeyboard/Info.plist

@@ -31,9 +31,5 @@
 		<key>NSExtensionPrincipalClass</key>
 		<string>$(PRODUCT_MODULE_NAME).KeyboardViewController</string>
 	</dict>
-	<key>UIAppFonts</key>
-	<array>
-		<string>tbmct.ttf</string>
-	</array>
 </dict>
 </plist>

+ 68 - 9
ios/AiKeyboard/KeyboardViewController.swift

@@ -241,6 +241,9 @@ class KeyboardViewController: UIInputViewController {
     
     var nowShowView: UIView?
     
+    // 命令检查定时器
+    private var guideCheckTimer: Timer?
+    
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         self.prepareHeightConstraint()
@@ -257,10 +260,16 @@ class KeyboardViewController: UIInputViewController {
         requestKeyboardList()
         requestPrologueList()
         startMonitoringPasteboard()
+        startListenGuide()
 //        self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
 //        self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
     }
     
+    override func viewWillDisappear(_ animated: Bool) {
+        
+        super.viewWillDisappear(animated)
+    }
+    
     override func viewWillLayoutSubviews() {
         super.viewWillLayoutSubviews()
     }
@@ -304,6 +313,34 @@ class KeyboardViewController: UIInputViewController {
             self.heightConstriaint?.constant = UX.keyboardHeight
         }
     }
+    
+    // 开始监听是否需要打开引导页
+    func startListenGuide() {
+        
+        // 创建定时器定期检查命令
+        guideCheckTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
+            self?.popGuideView()
+        }
+    }
+    
+    func popGuideView() {
+        
+        let hasFullAccess = self.hasFullAccess
+        let isNotFirstOpen = UserDefaults.standard.bool(forKey: "isNotFirstOpenKey")
+        let showGuide = KeyboardSharedDataManager.shared.getIsShowGuide()
+        if !isNotFirstOpen && hasFullAccess && (showGuide == true) {
+            
+            let guideView = KeyboardGuideView()
+            self.view.addSubview(guideView)
+            guideView.snp.makeConstraints { make in
+                make.top.left.right.bottom.equalTo(0)
+            }
+            UserDefaults.standard.set(true, forKey: "isNotFirstOpenKey")
+            
+            guideCheckTimer?.invalidate()
+            guideCheckTimer = nil
+        }
+    }
 }
 
 // MARK: - 网络请求
@@ -666,6 +703,11 @@ extension KeyboardViewController: KeyboardHelpViewDelegate, KeyboardTeachViewDel
         self.insertText(content)
     }
 
+    // 点击添加人设
+    func addCharacterJump() {
+        
+        self.marketBtnClickAction()
+    }
 }
 
 // MARK: - 处理键盘操作
@@ -684,12 +726,7 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
             if isShowing {
                 print("系统正在显示剪贴板权限弹窗")
                 self.view.toast(text: "请允许访问剪贴板")
-//                self.teachView.topView.isHidden = false
-                self.helpView.topView.isHidden = false
-                self.helpView.topView.snp.updateConstraints { make in
-                    make.height.equalTo(32)
-                }
-//                self.teachView.topView.isHidden = false
+                self.openPasteTipView()
             }
             if let content = self.getPasteboardContent() {
                 print("获取到剪贴板内容: \(content)")
@@ -876,6 +913,31 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
         return pasteboard.string
     }
     
+    // 允许粘贴跳转至设置页
+    func jumpToSettings() {
+        
+        if let url = URL(string: UIApplication.openSettingsURLString) {
+            self.openURL(url)
+        }
+    }
+    
+    func tipsCloseBtnAction() {
+        
+        self.helpView.topView.isHidden = true
+        self.helpView.topView.snp.updateConstraints { make in
+            make.height.equalTo(0)
+        }
+        
+        self.teachView.topView.isHidden = true
+        self.teachView.topView.snp.updateConstraints { make in
+            make.height.equalTo(0)
+        }
+        
+        self.prologueView.topView.isHidden = true
+        self.prologueView.topView.snp.updateConstraints { make in
+            make.height.equalTo(0)
+        }
+    }
 }
 
 extension KeyboardViewController {
@@ -913,9 +975,6 @@ extension KeyboardViewController {
                     self.openURL(url)
                 }
             }
-            tipView.backBtnClosure = {
-                
-            }
             self.view.addSubview(tipView)
             tipView.snp.makeConstraints { make in
                 make.left.right.bottom.equalTo(0)

+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "keyboard_guide_button@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "keyboard_guide_button@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/keyboard_guide_button@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_guide_button.imageset/keyboard_guide_button@3x.png


+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "keyboard_guide_image@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "keyboard_guide_image@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/keyboard_guide_image@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_guide_image.imageset/keyboard_guide_image@3x.png


+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "keyboard_vip_unlock_text@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "keyboard_vip_unlock_text@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/keyboard_vip_unlock_text@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_vip_unlock_text.imageset/keyboard_vip_unlock_text@3x.png


+ 1 - 0
ios/AiKeyboard/View/Cell/KeyboardCharacterCell.swift

@@ -39,6 +39,7 @@ class KeyboardCharacterCell: UICollectionViewCell {
     lazy var addBtn: UIButton = {
        
         let btn = UIButton(frame: CGRect(x: 0, y: 0, width: KeyboardHelpView.UX.cellWidth, height: KeyboardHelpView.UX.cellHeight))
+        btn.isEnabled = false
         btn.isHidden = true
         btn.setTitle("添加人设", for: .normal)
         btn.titleLabel?.font = .systemFont(ofSize: 12)

+ 14 - 4
ios/AiKeyboard/View/MainView/KeyboardBaseView.swift

@@ -20,6 +20,10 @@ protocol KeyboardBaseViewDelegate: NSObjectProtocol {
     // 长按删除方法
     func deleteBtnLongPressBegin()
     func deleteBtnLongPressEnd()
+    
+    func jumpToSettings()
+    
+    func tipsCloseBtnAction()
 }
 
 class KeyboardBaseView: UIView {
@@ -48,6 +52,10 @@ class KeyboardBaseView: UIView {
         label.text = "“允许”【从其他app粘贴】,不再弹出询问弹窗>"
         label.font = .systemFont(ofSize: 11)
         label.textColor = .hexStringColor(hexString: "#FFA616")
+        
+        label.isUserInteractionEnabled = true
+        let tap = UITapGestureRecognizer(target: self, action: #selector(settingTitleClickAction))
+        label.addGestureRecognizer(tap)
         return label
     }()
     
@@ -178,6 +186,11 @@ extension KeyboardBaseView {
 
 extension KeyboardBaseView {
     
+    @objc func settingTitleClickAction() {
+        
+        delegate?.jumpToSettings()
+    }
+    
     // 对话框清除按钮
     @objc func dialogueClearBtnAction() {
         
@@ -228,10 +241,7 @@ extension KeyboardBaseView {
     // 关闭提示框
     @objc func closeBtnClickAction() {
         
-        self.topView.snp.updateConstraints { make in
-            make.height.equalTo(0)
-        }
-        self.topView.isHidden = true
+        delegate?.tipsCloseBtnAction()
     }
 }
 

+ 24 - 12
ios/AiKeyboard/View/MainView/KeyboardHelpView.swift

@@ -10,6 +10,8 @@ import UIKit
 protocol KeyboardHelpViewDelegate: NSObjectProtocol {
     
     func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping ((Bool) -> ()))
+    
+    func addCharacterJump()
 }
 
 class KeyboardHelpView: KeyboardBaseView {
@@ -83,18 +85,24 @@ extension KeyboardHelpView: UICollectionViewDelegate, UICollectionViewDataSource
     
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
         
-        if self.dialogueLabel.text?.count == 0 {
-            self.toast(text: "请先粘贴文字")
-            return
-        }
-        
-        let cell = collectionView.cellForItem(at: indexPath) as! KeyboardCharacterCell
-        cell.setAnimate(play: true)
-        if let model = self.characterList?[indexPath.row], let characterId = model.id {
-            helpDelegate?.helpCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { isSuccess in
-                cell.setAnimate(play: false)
-                if isSuccess {
-                    self.dialogueClearBtnAction()
+        if indexPath.row == self.characterList?.count {
+            
+            helpDelegate?.addCharacterJump()
+        } else {
+            
+            if self.dialogueLabel.text?.count == 0 {
+                self.toast(text: "请先粘贴文字")
+                return
+            }
+            
+            let cell = collectionView.cellForItem(at: indexPath) as! KeyboardCharacterCell
+            cell.setAnimate(play: true)
+            if let model = self.characterList?[indexPath.row], let characterId = model.id {
+                helpDelegate?.helpCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { isSuccess in
+                    cell.setAnimate(play: false)
+                    if isSuccess {
+                        self.dialogueClearBtnAction()
+                    }
                 }
             }
         }
@@ -112,6 +120,10 @@ extension KeyboardHelpView: UICollectionViewDelegate, UICollectionViewDataSource
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
         return 6
     }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+        return CGSize(width: KeyboardConst.kb_kScreenW, height: 6)
+    }
 }
 
 extension KeyboardHelpView {

+ 4 - 0
ios/AiKeyboard/View/MainView/KeyboardPrologueView.swift

@@ -273,6 +273,10 @@ extension KeyboardPrologueView: UICollectionViewDelegate, UICollectionViewDataSo
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
         return 6
     }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+        return CGSize(width: KeyboardConst.kb_kScreenW, height: 6)
+    }
 }
 
 extension KeyboardPrologueView: UITableViewDelegate, UITableViewDataSource {

+ 31 - 19
ios/AiKeyboard/View/MainView/KeyboardTeachView.swift

@@ -12,6 +12,8 @@ protocol KeyboardTeachViewDelegate: NSObjectProtocol {
     func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String], Bool, Bool) -> ()))
     
     func teachTableViewDidSelectItem(content: String)
+    
+    func addCharacterJump()
 }
 
 class KeyboardTeachView: KeyboardBaseView {
@@ -181,28 +183,34 @@ extension KeyboardTeachView: UICollectionViewDelegate, UICollectionViewDataSourc
     
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
         
-        if self.dialogueLabel.text?.count == 0 {
-            self.toast(text: "请先粘贴文字")
-            return
-        }
-        
-        if let model = self.characterList?[indexPath.row], let characterId = model.id {
+        if indexPath.row == self.characterList?.count {
             
-            self.selectCharacter = model
-            teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list, isLogin, isLoaded in
-                
-                if isLogin {
-                    // 清空回答列表
-                    self.resultList?.removeAll()
-                    self.collectionView.isHidden = true
-                    self.resultView.isHidden = false
-                    self.tableView.reloadData()
-                }
+            teachDelegate?.addCharacterJump()
+        } else {
+            
+            if self.dialogueLabel.text?.count == 0 {
+                self.toast(text: "请先粘贴文字")
+                return
+            }
+            
+            if let model = self.characterList?[indexPath.row], let characterId = model.id {
                 
-                if isLoaded {
+                self.selectCharacter = model
+                teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list, isLogin, isLoaded in
                     
-                    self.isResultLoading = false
-                    self.resultList = list
+                    if isLogin {
+                        // 清空回答列表
+                        self.resultList?.removeAll()
+                        self.collectionView.isHidden = true
+                        self.resultView.isHidden = false
+                        self.tableView.reloadData()
+                    }
+                    
+                    if isLoaded {
+                        
+                        self.isResultLoading = false
+                        self.resultList = list
+                    }
                 }
             }
         }
@@ -220,6 +228,10 @@ extension KeyboardTeachView: UICollectionViewDelegate, UICollectionViewDataSourc
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
         return 6
     }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
+        return CGSize(width: KeyboardConst.kb_kScreenW, height: 6)
+    }
 }
 
 extension KeyboardTeachView: UITableViewDelegate, UITableViewDataSource {

+ 77 - 0
ios/AiKeyboard/View/PopView/KeyboardGuideView.swift

@@ -0,0 +1,77 @@
+//
+//  KeyboardGuideView.swift
+//  AiKeyboard
+//
+//  Created by Destiny on 2025/5/13.
+//
+
+import UIKit
+
+class KeyboardGuideView: UIView {
+    
+    lazy var bgView: UIView = {
+       
+        let view = UIView()
+        view.backgroundColor = .hexStringColor(hexString: "#000000").withAlphaComponent(0.8)
+        return view
+    }()
+    
+    lazy var buttonImageView: UIImageView = {
+       
+        let imageView = UIImageView()
+        imageView.image = UIImage(named: "keyboard_guide_button")
+        
+        imageView.isUserInteractionEnabled = true
+        let tap = UITapGestureRecognizer(target: self, action: #selector(removeViewAction))
+        imageView.addGestureRecognizer(tap)
+        
+        return imageView
+    }()
+    
+    lazy var guideImageView: UIImageView = {
+       
+        let imageView = UIImageView()
+        imageView.image = UIImage(named: "keyboard_guide_image")
+        return imageView
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        initUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @objc func removeViewAction() {
+        
+        self.removeFromSuperview()
+    }
+}
+
+extension KeyboardGuideView {
+    
+    func initUI() {
+        
+        self.addSubview(bgView)
+        bgView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        self.bgView.addSubview(guideImageView)
+        guideImageView.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 180, height: 150))
+            make.right.equalTo(-12)
+            make.top.equalTo(12)
+        }
+        
+        let left = KeyboardHelpView.UX.cellWidth + 24
+        self.bgView.addSubview(buttonImageView)
+        buttonImageView.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 88, height: 46))
+            make.top.equalTo(110)
+            make.left.equalTo(left)
+        }
+    }
+}

+ 9 - 2
ios/AiKeyboard/View/PopView/KeyboardVipTipView.swift

@@ -70,6 +70,13 @@ class KeyboardVipTipView: UIView {
         return animate
     }()
     
+    lazy var unlockTextImageView: UIImageView = {
+       
+        let imageView = UIImageView()
+        imageView.image = UIImage(named: "keyboard_vip_unlock_text")
+        return imageView
+    }()
+    
     lazy var unlockLabel: UILabel = {
         
         // 注册自定义字体
@@ -164,8 +171,8 @@ extension KeyboardVipTipView {
         }
         unlockBtnAnimation.play()
         
-        unlockBtnAnimation.addSubview(unlockLabel)
-        unlockLabel.snp.makeConstraints { make in
+        unlockBtnAnimation.addSubview(unlockTextImageView)
+        unlockTextImageView.snp.makeConstraints { make in
             make.center.equalToSuperview()
         }
     }

+ 9 - 0
ios/KeyboardSharedDataManager.swift

@@ -60,6 +60,15 @@ class KeyboardSharedDataManager {
         return sharedUserDefaults?.string(forKey: "init_idfa")
     }
     
+    func saveIsShowGuide() {
+        sharedUserDefaults?.set(true, forKey: "is_show_guide")
+        sharedUserDefaults?.synchronize()
+    }
+    
+    func getIsShowGuide() -> Bool? {
+        return sharedUserDefaults?.bool(forKey: "is_show_guide")
+    }
+    
     // 检查是否已登录
     func isLoggedIn() -> Bool {
         return getToken() != nil

+ 6 - 0
ios/Podfile.lock

@@ -4,6 +4,8 @@ PODS:
     - Flutter
   - app_tracking_transparency (0.0.1):
     - Flutter
+  - apple_pay (0.0.1):
+    - Flutter
   - audio_session (0.0.1):
     - Flutter
   - Bugly (2.6.1)
@@ -82,6 +84,7 @@ PODS:
 DEPENDENCIES:
   - app_settings (from `.symlinks/plugins/app_settings/ios`)
   - app_tracking_transparency (from `.symlinks/plugins/app_tracking_transparency/ios`)
+  - apple_pay (from `.symlinks/plugins/apple_pay/ios`)
   - audio_session (from `.symlinks/plugins/audio_session/ios`)
   - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
   - Flutter (from `Flutter`)
@@ -132,6 +135,8 @@ EXTERNAL SOURCES:
     :path: ".symlinks/plugins/app_settings/ios"
   app_tracking_transparency:
     :path: ".symlinks/plugins/app_tracking_transparency/ios"
+  apple_pay:
+    :path: ".symlinks/plugins/apple_pay/ios"
   audio_session:
     :path: ".symlinks/plugins/audio_session/ios"
   device_info_plus:
@@ -177,6 +182,7 @@ SPEC CHECKSUMS:
   Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
   app_settings: 58017cd26b604ae98c3e65acbdd8ba173703cc82
   app_tracking_transparency: e169b653478da7bb15a6c61209015378ca73e375
+  apple_pay: 631cbd73016e371a5f3f31c1d9fa2444740e80be
   audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe
   Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
   device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342

+ 4 - 6
ios/Runner.xcodeproj/project.pbxproj

@@ -56,8 +56,7 @@
 		FE05FA2E2DCE0CFC00CBA944 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FE05FA2D2DCE0CFC00CBA944 /* Settings.bundle */; };
 		FE8F000A2DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8F00092DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift */; };
 		FED899712DD1D044001808D5 /* KeyboardTipsPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */; };
-		FED899762DD1F5D5001808D5 /* tbmct.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FED899752DD1F5D5001808D5 /* tbmct.ttf */; };
-		FED899772DD1F5D5001808D5 /* tbmct.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FED899752DD1F5D5001808D5 /* tbmct.ttf */; };
+		FED899792DD3446A001808D5 /* KeyboardGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED899782DD3446A001808D5 /* KeyboardGuideView.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -170,7 +169,7 @@
 		FE8F00052DCC4A7B00AA2562 /* AiKeyboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AiKeyboard.entitlements; sourceTree = "<group>"; };
 		FE8F00092DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlutterMethodChannelManager.swift; sourceTree = "<group>"; };
 		FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTipsPopView.swift; sourceTree = "<group>"; };
-		FED899752DD1F5D5001808D5 /* tbmct.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = tbmct.ttf; sourceTree = "<group>"; };
+		FED899782DD3446A001808D5 /* KeyboardGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardGuideView.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -288,6 +287,7 @@
 				04582A152DBB681100C1792D /* KeyboardVipTipView.swift */,
 				04582A1E2DBF227900C1792D /* KeyboardMenuView.swift */,
 				FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */,
+				FED899782DD3446A001808D5 /* KeyboardGuideView.swift */,
 			);
 			path = PopView;
 			sourceTree = "<group>";
@@ -372,7 +372,6 @@
 		97C146E51CF9000F007C117D = {
 			isa = PBXGroup;
 			children = (
-				FED899752DD1F5D5001808D5 /* tbmct.ttf */,
 				043D62362DCB0E7A005D12EE /* KeyboardSharedDataManager.swift */,
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
@@ -531,7 +530,6 @@
 				04582A512DC211F100C1792D /* keyboard_dialog_loading.json in Resources */,
 				04582A322DBF6C0800C1792D /* heart.json in Resources */,
 				045828C92DB89ABE00C1792D /* Media.xcassets in Resources */,
-				FED899772DD1F5D5001808D5 /* tbmct.ttf in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -551,7 +549,6 @@
 				FE05FA2E2DCE0CFC00CBA944 /* Settings.bundle in Resources */,
 				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
 				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
-				FED899762DD1F5D5001808D5 /* tbmct.ttf in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -718,6 +715,7 @@
 				04582A012DB8EBC700C1792D /* KeyboardConst.swift in Sources */,
 				04582A122DBB642C00C1792D /* UILabel+Extension.swift in Sources */,
 				04582A372DBF6F8300C1792D /* KeyboardExchangeCell.swift in Sources */,
+				FED899792DD3446A001808D5 /* KeyboardGuideView.swift in Sources */,
 				045828CC2DB8C62100C1792D /* UIColor+Extension.swift in Sources */,
 				04582A4B2DC0BDA100C1792D /* CharacterModel.swift in Sources */,
 				04582A102DBB39A700C1792D /* KeyboardBaseView.swift in Sources */,

+ 1 - 1
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@@ -51,7 +51,7 @@
       </Testables>
    </TestAction>
    <LaunchAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "Release"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"

+ 80 - 17
ios/Runner/AppDelegate.swift

@@ -12,31 +12,94 @@ import UIKit
       let controller = window?.rootViewController as! FlutterViewController
       FlutterMethodChannelManager.shared.setupMethodChannels(controller: controller)
       
+      // 创建事件通道
+      let keyboardEventChannel = FlutterEventChannel(name: "keyboard_ios_events",
+                                                        binaryMessenger: controller.binaryMessenger)
+      // 设置事件处理器
+      let keyboardStreamHandler = KeyboardStreamHandler()
+      keyboardEventChannel.setStreamHandler(keyboardStreamHandler)
+      // 开始监听键盘切换
+      startMonitoringKeyboardChanges()
+      
       return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }
     
     override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
         
-        // 解析URL参数
-        let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
         let path = url.path
-        
-        // 将URL信息传递给Flutter
-        let controller = window?.rootViewController as! FlutterViewController
-        let channel = FlutterMethodChannel(name: "keyboard_ios", binaryMessenger: controller.binaryMessenger)
-        
-        if path == "/login" {
-            channel.invokeMethod("navigateToLogin", arguments: nil)
-        } else if path == "/member" {
-            channel.invokeMethod("navigateToMember", arguments: nil)
-        } else if path == "/character/market" {
-            channel.invokeMethod("navigateToCharacterMarket", arguments: nil)
-        } else if path == "/intimacy" {
-            channel.invokeMethod("navigateToIntimacy", arguments: nil)
-        } else if path == "/character/custom" {
-            channel.invokeMethod("navigateToCustomCharacter", arguments: nil)
+        if let channel = FlutterMethodChannelManager.shared.keyboardChannel {
+            if path == "/login" {
+                channel.invokeMethod("navigateToLogin", arguments: nil)
+            } else if path == "/member" {
+                channel.invokeMethod("navigateToMember", arguments: nil)
+            } else if path == "/character/market" {
+                channel.invokeMethod("navigateToCharacterMarket", arguments: nil)
+            } else if path == "/intimacy" {
+                channel.invokeMethod("navigateToIntimacy", arguments: nil)
+            } else if path == "/character/custom" {
+                channel.invokeMethod("navigateToCustomCharacter", arguments: nil)
+            }
         }
         
         return true
     }
+    
+    func startMonitoringKeyboardChanges() {
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidChange), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil)
+    }
+
+    @objc func keyboardDidChange() {
+        // 收到通知后延迟检测
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
+            if let isCustom = self?.isCustomKeybroad() {
+                // 发送通知
+                NotificationCenter.default.post(name: NSNotification.Name("KeyboardDidChange"), object: nil, userInfo: ["isCustomKeyboard": isCustom])
+            }
+        }
+    }
+    
+    func isCustomKeybroad() -> Bool {
+        
+        let currentKeyboardName = (((UITextInputMode.activeInputModes as NSArray).filtered(using: NSPredicate(format: "isDisplayed = YES"))).last as? NSObject)?.value(forKey: "extendedDisplayName") as? String
+        
+        let infoDictionary = Bundle.main.infoDictionary!
+        let appDisplayName = infoDictionary["CFBundleDisplayName"] as? String
+        
+        return currentKeyboardName == appDisplayName
+    }
+}
+
+
+// 事件流处理器
+class KeyboardStreamHandler: NSObject, FlutterStreamHandler {
+    private var eventSink: FlutterEventSink?
+
+    override init() {
+        super.init()
+        // 注册通知
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidChange(_:)), name: NSNotification.Name("KeyboardDidChange"), object: nil)
+    }
+
+    @objc func keyboardDidChange(_ notification: Notification) {
+        if let userInfo = notification.userInfo,
+           let isCustomKeyboard = userInfo["isCustomKeyboard"] as? Bool,
+           let eventSink = eventSink {
+            // 发送事件到Flutter
+            eventSink(isCustomKeyboard)
+        }
+    }
+
+    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
+        eventSink = events
+        return nil
+    }
+
+    func onCancel(withArguments arguments: Any?) -> FlutterError? {
+        eventSink = nil
+        return nil
+    }
+
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
 }

+ 36 - 1
ios/Runner/FlutterMethodChannelManager.swift

@@ -14,7 +14,7 @@ class FlutterMethodChannelManager: NSObject {
     static let shared = FlutterMethodChannelManager()
     
     // 方法通道
-    private var keyboardChannel: FlutterMethodChannel?
+    var keyboardChannel: FlutterMethodChannel?
     
     // 私有初始化方法
     private override init() {
@@ -60,6 +60,15 @@ class FlutterMethodChannelManager: NSObject {
                 } else {
                     result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
                 }
+            case "isKeyboardAdded":
+                let isAdd = self.isKeyboardEnabled()
+                result(isAdd)
+            case "isDefaultKeyboard":
+                let isDefault = self.isCustomKeybroad()
+                result(isDefault)
+            case "openKeyboardGuide":
+                KeyboardSharedDataManager.shared.saveIsShowGuide()
+                result(true)
             default:
                 result(FlutterMethodNotImplemented)
             }
@@ -82,4 +91,30 @@ class FlutterMethodChannelManager: NSObject {
         let userDefaults = UserDefaults(suiteName: "group.com.yourcompany.aikeyboard")
         return userDefaults?.string(forKey: "auth_token")
     }
+    
+    // 是否添加键盘
+    func isKeyboardEnabled() -> Bool {
+        
+        // 获取系统中已安装的键盘列表
+        let keyboards = UserDefaults.standard.dictionaryRepresentation()
+            .filter { $0.key.contains("Keyboard") }
+        if let keyboardList = keyboards["AppleKeyboards"] as? [String] {
+            
+            let keyboardBundleId = "com.qihuan.zhuiaijianpan.AiKeyboard"
+            let isAdded = keyboardList.contains(keyboardBundleId)
+            return isAdded
+        }
+        return false
+    }
+    
+    // 是否为默认键盘
+    func isCustomKeybroad() -> Bool {
+        
+        let currentKeyboardName = (((UITextInputMode.activeInputModes as NSArray).filtered(using: NSPredicate(format: "isDisplayed = YES"))).last as? NSObject)?.value(forKey: "extendedDisplayName") as? String
+        
+        let infoDictionary = Bundle.main.infoDictionary!
+        let appDisplayName = infoDictionary["CFBundleDisplayName"] as? String
+        
+        return currentKeyboardName == appDisplayName
+    }
 }

+ 6 - 0
ios/Runner/Info.plist

@@ -2,6 +2,12 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>LSApplicationQueriesSchemes</key>
+	<array>
+		<string>weixin</string>
+		<string>weixinULAPI</string>
+		<string>weixinURLParamsAPI</string>
+	</array>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>


+ 2 - 1
lib/data/api/request/subscribe_resume_request.dart

@@ -1,12 +1,13 @@
 import 'package:json_annotation/json_annotation.dart';
 
+import '../../../base/app_base_request.dart';
 import '../../../base/base_request.dart';
 
 part 'subscribe_resume_request.g.dart';
 
 /// 恢复订阅(IOS使用)
 @JsonSerializable()
-class SubscribeResumeRequest extends BaseRequest {
+class SubscribeResumeRequest extends AppBaseRequest {
   @JsonKey(name: 'receiptData')
   String receiptData;
 

+ 3 - 1
lib/data/api/request/subscribe_resume_request.g.dart

@@ -38,7 +38,8 @@ SubscribeResumeRequest _$SubscribeResumeRequestFromJson(
       ..wifiName = json['wifiName'] as String?
       ..region = json['region'] as String?
       ..locLng = (json['locLng'] as num?)?.toDouble()
-      ..locLat = (json['locLat'] as num?)?.toDouble();
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
 
 Map<String, dynamic> _$SubscribeResumeRequestToJson(
   SubscribeResumeRequest instance,
@@ -68,6 +69,7 @@ Map<String, dynamic> _$SubscribeResumeRequestToJson(
   'region': instance.region,
   'locLng': instance.locLng,
   'locLat': instance.locLat,
+  'authToken': instance.authToken,
   'receiptData': instance.receiptData,
   'payPlatform': instance.payPlatform,
   'payMethod': instance.payMethod,

+ 4 - 0
lib/data/bean/goods_info.dart

@@ -11,6 +11,8 @@ class GoodsInfo {
   late int sort;
   @JsonKey(name: "name")
   late String name;
+  @JsonKey(name: "appleGoodsId")
+  late String appleGoodsId;
   @JsonKey(name: "subscribable")
   late int subscribable;
   @JsonKey(name: "amount")
@@ -40,6 +42,7 @@ class GoodsInfo {
     required this.id,
     required this.sort,
     required this.name,
+    required this.appleGoodsId,
     required this.subscribable,
     required this.amount,
     required this.originalAmount,
@@ -72,6 +75,7 @@ class GoodsInfo {
       id: id,
       sort: sort,
       name: name,
+      appleGoodsId: appleGoodsId,
       subscribable: subscribable,
       amount: amount,
       originalAmount: originalAmount,

+ 2 - 0
lib/data/bean/goods_info.g.dart

@@ -10,6 +10,7 @@ GoodsInfo _$GoodsInfoFromJson(Map<String, dynamic> json) => GoodsInfo(
   id: (json['id'] as num).toInt(),
   sort: (json['sort'] as num).toInt(),
   name: json['name'] as String,
+  appleGoodsId: json['appleGoodsId'] as String,
   subscribable: (json['subscribable'] as num).toInt(),
   amount: (json['amount'] as num).toInt(),
   originalAmount: (json['originalAmount'] as num).toInt(),
@@ -31,6 +32,7 @@ Map<String, dynamic> _$GoodsInfoToJson(GoodsInfo instance) => <String, dynamic>{
   'id': instance.id,
   'sort': instance.sort,
   'name': instance.name,
+  'appleGoodsId': instance.appleGoodsId,
   'subscribable': instance.subscribable,
   'amount': instance.amount,
   'originalAmount': instance.originalAmount,

+ 3 - 1
lib/data/consts/payment_type.dart

@@ -23,7 +23,7 @@ class PayWayType {
   static const int paymentWayWechatScan = 2;
   static const int paymentWayAlipay = 3;
   static const int paymentWayAlipayScan = 4;
-
+  static const int paymentWayApple = 5;
   static const int unKnown = -1;
 }
 
@@ -49,6 +49,8 @@ int getPayWayType({required int payMethod, required int payPlatform}) {
     return PayWayType.paymentWayAlipay;
   } else if (payPlatform == 4 && payMethod == 1) {
     return PayWayType.paymentWayAlipayScan;
+  } else if (payPlatform == 2 && payMethod == 3) {
+    return PayWayType.paymentWayApple;
   }
   return PayWayType.unKnown;
 }

+ 5 - 0
lib/data/consts/web_url.dart

@@ -26,6 +26,9 @@ class WebUrl {
   static const String _thirdPartyList =
       "https://doc.v8dashen.com/doc/35b71c0368d01923";
 
+  // 会员服务协议
+  static const String _subscribeAgreement = "https://doc.v8dashen.com/doc/e689ca2786339982";
+
   static const String _qiyuService ="https://qiyu-kefu.atmob.com";
 
 
@@ -37,6 +40,8 @@ class WebUrl {
 
   static String get thirdPartyList => _thirdPartyList;
 
+  static String get subscribeAgreement => _subscribeAgreement;
+
   static String get personalInformationList => _personalInformationList;
 
   static String get qiyuService => _qiyuService;

+ 1 - 0
lib/data/repository/store_repository.dart

@@ -4,6 +4,7 @@ import 'package:keyboard/data/api/response/item_list_response.dart';
 import 'package:keyboard/data/api/response/order_pay_response.dart';
 import 'package:keyboard/utils/http_handler.dart';
 import 'package:get/get.dart';
+import 'package:keyboard/widget/platform_util.dart';
 import '../../base/app_base_request.dart';
 import '../../utils/async_util.dart';
 import '../../utils/atmob_log.dart';

+ 42 - 42
lib/di/get_it.config.dart

@@ -118,62 +118,62 @@ extension GetItInjectableX on _i174.GetIt {
   }) {
     final gh = _i526.GetItHelper(this, environment, environmentFilter);
     final networkModule = _$NetworkModule();
-    gh.factory<_i256.AboutController>(() => _i256.AboutController());
-    gh.factory<_i923.BrowserController>(() => _i923.BrowserController());
-    gh.factory<_i1057.ChangeBirthdayController>(
-      () => _i1057.ChangeBirthdayController(),
+    gh.factory<_i415.KeyboardMethodHandler>(
+      () => _i415.KeyboardMethodHandler(),
     );
     gh.factory<_i315.ChangeGenderController>(
       () => _i315.ChangeGenderController(),
     );
+    gh.factory<_i1057.ChangeBirthdayController>(
+      () => _i1057.ChangeBirthdayController(),
+    );
     gh.factory<_i859.ChangeNicknameController>(
       () => _i859.ChangeNicknameController(),
     );
-    gh.factory<_i1071.ImageViewerController>(
-      () => _i1071.ImageViewerController(),
-    );
+    gh.factory<_i973.SplashController>(() => _i973.SplashController());
+    gh.factory<_i211.IntroController>(() => _i211.IntroController());
+    gh.factory<_i256.AboutController>(() => _i256.AboutController());
     gh.factory<_i977.IntimacyAnalyseController>(
       () => _i977.IntimacyAnalyseController(),
     );
-    gh.factory<_i873.IntimacyGenerateCharacterEditController>(
-      () => _i873.IntimacyGenerateCharacterEditController(),
-    );
     gh.factory<_i279.IntimacyAnalyseScreenshotReplyController>(
       () => _i279.IntimacyAnalyseScreenshotReplyController(),
     );
-    gh.factory<_i211.IntroController>(() => _i211.IntroController());
+    gh.factory<_i1071.ImageViewerController>(
+      () => _i1071.ImageViewerController(),
+    );
+    gh.factory<_i873.IntimacyGenerateCharacterEditController>(
+      () => _i873.IntimacyGenerateCharacterEditController(),
+    );
+    gh.factory<_i923.BrowserController>(() => _i923.BrowserController());
     gh.factory<_i248.KeyboardGuidePageController>(
       () => _i248.KeyboardGuidePageController(),
     );
-    gh.factory<_i1022.KeyboardTutorialAndroidViewController>(
-      () => _i1022.KeyboardTutorialAndroidViewController(),
+    gh.factory<_i1060.ZodiacLoveIntimacyController>(
+      () => _i1060.ZodiacLoveIntimacyController(),
     );
-    gh.factory<_i842.KeyboardTutorialIosViewController>(
-      () => _i842.KeyboardTutorialIosViewController(),
+    gh.factory<_i101.KeyboardTutorialVideoController>(
+      () => _i101.KeyboardTutorialVideoController(),
     );
-    gh.factory<_i507.KeyboardTutorialController>(
-      () => _i507.KeyboardTutorialController(),
+    gh.factory<_i421.KeyboardTutorialVideoIosViewController>(
+      () => _i421.KeyboardTutorialVideoIosViewController(),
     );
     gh.factory<_i925.KeyboardTutorialVideoAndroidViewController>(
       () => _i925.KeyboardTutorialVideoAndroidViewController(),
     );
-    gh.factory<_i421.KeyboardTutorialVideoIosViewController>(
-      () => _i421.KeyboardTutorialVideoIosViewController(),
+    gh.factory<_i507.KeyboardTutorialController>(
+      () => _i507.KeyboardTutorialController(),
     );
-    gh.factory<_i101.KeyboardTutorialVideoController>(
-      () => _i101.KeyboardTutorialVideoController(),
+    gh.factory<_i842.KeyboardTutorialIosViewController>(
+      () => _i842.KeyboardTutorialIosViewController(),
+    );
+    gh.factory<_i1022.KeyboardTutorialAndroidViewController>(
+      () => _i1022.KeyboardTutorialAndroidViewController(),
     );
-    gh.factory<_i973.SplashController>(() => _i973.SplashController());
     gh.factory<_i333.DiscountController>(() => _i333.DiscountController());
     gh.factory<_i827.RecoverSubscribeController>(
       () => _i827.RecoverSubscribeController(),
     );
-    gh.factory<_i1060.ZodiacLoveIntimacyController>(
-      () => _i1060.ZodiacLoveIntimacyController(),
-    );
-    gh.factory<_i415.KeyboardMethodHandler>(
-      () => _i415.KeyboardMethodHandler(),
-    );
     gh.lazySingleton<_i495.WechatLoginService>(
       () => _i495.WechatLoginService(),
     );
@@ -224,15 +224,15 @@ extension GetItInjectableX on _i174.GetIt {
     gh.lazySingleton<_i428.UploadFileManager>(
       () => _i428.UploadFileManager(gh<_i815.FileUploadRepository>()),
     );
+    gh.lazySingleton<_i50.ConfigRepository>(
+      () => _i50.ConfigRepository(gh<_i243.AtmobApi>()),
+    );
     gh.lazySingleton<_i83.AccountRepository>(
       () => _i83.AccountRepository(gh<_i243.AtmobApi>()),
     );
     gh.lazySingleton<_i421.CharactersRepository>(
       () => _i421.CharactersRepository(gh<_i243.AtmobApi>()),
     );
-    gh.lazySingleton<_i50.ConfigRepository>(
-      () => _i50.ConfigRepository(gh<_i243.AtmobApi>()),
-    );
     gh.lazySingleton<_i274.KeyboardRepository>(
       () => _i274.KeyboardRepository(gh<_i243.AtmobApi>()),
     );
@@ -272,8 +272,8 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i243.AtmobApi>(),
       ),
     );
-    gh.factory<_i161.KeyBoardController>(
-      () => _i161.KeyBoardController(
+    gh.factory<_i244.ProfileController>(
+      () => _i244.ProfileController(
         gh<_i274.KeyboardRepository>(),
         gh<_i83.AccountRepository>(),
       ),
@@ -284,8 +284,8 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i83.AccountRepository>(),
       ),
     );
-    gh.factory<_i244.ProfileController>(
-      () => _i244.ProfileController(
+    gh.factory<_i161.KeyBoardController>(
+      () => _i161.KeyBoardController(
         gh<_i274.KeyboardRepository>(),
         gh<_i83.AccountRepository>(),
       ),
@@ -394,12 +394,12 @@ extension GetItInjectableX on _i174.GetIt {
         currentKeyboardInfo: currentKeyboardInfo,
       ),
     );
-    gh.factory<_i84.ChangeCharacterController>(
-      () => _i84.ChangeCharacterController(gh<_i50.ConfigRepository>()),
-    );
     gh.factory<_i765.ChangeHobbiesController>(
       () => _i765.ChangeHobbiesController(gh<_i50.ConfigRepository>()),
     );
+    gh.factory<_i84.ChangeCharacterController>(
+      () => _i84.ChangeCharacterController(gh<_i50.ConfigRepository>()),
+    );
     gh.factory<_i15.CharacterCustomController>(
       () => _i15.CharacterCustomController(gh<_i50.ConfigRepository>()),
     );
@@ -423,16 +423,16 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i83.AccountRepository>(),
       ),
     );
-    gh.factory<_i510.ConversationAnalysisController>(
-      () => _i510.ConversationAnalysisController(
+    gh.factory<_i464.ScanImageReplyController>(
+      () => _i464.ScanImageReplyController(
         gh<_i738.IntimacyAnalyzeConfigHelper>(),
         gh<_i428.UploadFileManager>(),
         gh<_i283.IntimacyAnalyzeRepository>(),
         gh<_i83.AccountRepository>(),
       ),
     );
-    gh.factory<_i464.ScanImageReplyController>(
-      () => _i464.ScanImageReplyController(
+    gh.factory<_i510.ConversationAnalysisController>(
+      () => _i510.ConversationAnalysisController(
         gh<_i738.IntimacyAnalyzeConfigHelper>(),
         gh<_i428.UploadFileManager>(),
         gh<_i283.IntimacyAnalyzeRepository>(),

+ 1 - 1
lib/handler/wechat_login_service.dart

@@ -11,7 +11,7 @@ import '../utils/atmob_log.dart';
 class WechatLoginService {
   final String tag = "WechatLoginService";
   final String _appId = "wx21272929e8fd33e9"; //AppID
-  final String? _universalLink = "wx21272929e8fd33e9"; // universalLink
+  final String? _universalLink = "https://youyue.shop"; // universalLink
 
  late final StreamSubscription<WechatResp> _respSub;
 

+ 2 - 0
lib/main.dart

@@ -17,6 +17,7 @@ import 'package:keyboard/sdk/gravity/gravity_helper.dart';
 import 'package:keyboard/utils/app_info_util.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 import 'package:keyboard/utils/channel_util.dart';
+import 'package:keyboard/utils/default_keyboard_helper.dart';
 import 'package:keyboard/utils/floating_window_helper.dart';
 import 'package:keyboard/utils/mmkv_util.dart';
 import 'package:keyboard/utils/method_chanel_ios_util.dart';
@@ -25,6 +26,7 @@ import 'package:keyboard/utils/toast_util.dart';
 import 'package:keyboard/widget/app_lifecycle_widget.dart';
 import 'package:keyboard/sdk/umeng/umeng_helper.dart';
 import 'package:lottie/lottie.dart';
+import 'package:keyboard/widget/platform_util.dart';
 import 'data/consts/build_config.dart';
 import 'data/consts/constants.dart';
 import 'device/device_info_util.dart';

+ 11 - 2
lib/module/keyboard_guide/keyboard_guide_controller.dart

@@ -3,6 +3,9 @@ import 'package:get/get.dart';
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/module/keyboard_guide/enums/keyboard_guide_msg_type.dart';
 import 'package:keyboard/resource/string.gen.dart';
+import 'package:keyboard/utils/keyboard_detect_ios_util.dart';
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
+import 'package:keyboard/widget/platform_util.dart';
 import '../../base/base_controller.dart';
 import '../../data/bean/keyboard_guide_msg.dart';
 import '../../dialog/login/login_dialog.dart';
@@ -39,6 +42,8 @@ class KeyboardGuidePageController extends BaseController {
   /// 是否选择为了默认键盘
   RxBool get isDefaultKeyboard => DefaultKeyboardHelper.isDefaultKeyboard;
 
+  RxBool get isIOS => RxBool(PlatformUtil.isIOS);
+
   /// 是否有悬浮窗权限
   RxBool get hasFloatingWindowPermission =>
       FloatingWindowHelper.hasFloatingWindowPermission;
@@ -51,7 +56,7 @@ class KeyboardGuidePageController extends BaseController {
     // 初始化消息列表
     _initMsgList();
     checkSetting();
-
+    KeyboardDetectIOSUtil.initialize();
     // 进入页面,就获取输入框焦点
     // requestInputFocus();
   }
@@ -64,6 +69,7 @@ class KeyboardGuidePageController extends BaseController {
     inputFocusNode.dispose();
     editingController.dispose();
     scrollController.dispose();
+    KeyboardDetectIOSUtil.dispose();
     super.onClose();
   }
 
@@ -85,7 +91,9 @@ class KeyboardGuidePageController extends BaseController {
     //   KeyboardTutorialPage.startAndOffMe();
     // }
     // 有权限,则马上显示悬浮窗
-    FloatingWindowHelper.showFloatingBall();
+    if (PlatformUtil.isAndroid) {
+      FloatingWindowHelper.showFloatingBall();
+    }
   }
 
   /// 关闭页面
@@ -221,6 +229,7 @@ class KeyboardGuidePageController extends BaseController {
           // 复制
           ClipboardUtil.copyToClipboard(StringName.keyboardGuideTaReply2);
           ToastUtil.show(StringName.copySuccess);
+          MethodChanelIOSUtil.openKeyboardGuide();
         },
         onFinishCallback: () {
           // 设置未非首次显示

+ 19 - 4
lib/module/keyboard_guide/keyboard_guide_page.dart

@@ -56,6 +56,16 @@ class KeyboardGuidePage extends BasePage<KeyboardGuidePageController> {
         },
         child: Column(
           children: [
+            // 使用 Obx 监听 isDefaultKeyboard 的变化
+            Obx(() {
+              // 当 isDefaultKeyboard 变为 true 时,显示引导对话框
+              if (controller.isDefaultKeyboard.value) {
+                // 使用 Future.microtask 确保在构建完成后调用
+                Future.microtask(() => controller.showGuideOverlayDialog());
+              }
+              // 返回一个空的 SizedBox,不影响 UI
+              return SizedBox.shrink();
+            }),
             // 标题栏
             _buildTitleBar(),
             // 消息列表
@@ -204,7 +214,10 @@ class KeyboardGuidePage extends BasePage<KeyboardGuidePageController> {
                     child: Obx(() {
                       return TextField(
                         // 是否可用,选择了默认键盘时,才可用
-                        enabled: controller.isDefaultKeyboard.value,
+                        enabled:
+                            controller.isIOS.value
+                                ? true
+                                : controller.isDefaultKeyboard.value,
                         // 是否自动获取焦点
                         autofocus: false,
                         style: TextStyle(
@@ -230,9 +243,11 @@ class KeyboardGuidePage extends BasePage<KeyboardGuidePageController> {
                         // 输入框焦点
                         focusNode: controller.inputFocusNode,
                         // 点击外部区域,关闭软键盘
-                        // onTapUpOutside: (event) {
-                        //   controller.clearInputFocus();
-                        // },
+                        onTapUpOutside: (event) {
+                          // if (PlatformUtil.isIOS)  {
+                          //   controller.clearInputFocus();
+                          // }
+                        },
                         // 输入框控制器
                         controller: controller.editingController,
                         decoration: InputDecoration(

+ 91 - 25
lib/module/store/new_discount/new_discount_controller.dart

@@ -10,6 +10,7 @@ import 'package:keyboard/base/base_controller.dart';
 import 'package:get/get.dart';
 import 'package:keyboard/data/bean/character_info.dart';
 import 'package:keyboard/module/store/new_discount/member_card_bean.dart';
+import 'package:keyboard/widget/platform_util.dart';
 import '../../../data/api/response/user_info_response.dart';
 import '../../../data/bean/goods_info.dart';
 import '../../../data/bean/member_info.dart';
@@ -270,11 +271,19 @@ class NewDiscountController extends BaseController
               buyPayWay,
               buyGoods,
             );
+          } else if (payWayType == PayWayType.paymentWayApple) {
+            _onApplePay(
+              response.outTradeNo,
+              response.appAccountToken ?? "",
+              buyPayWay,
+              buyGoods,
+            );
           } else {
             ToastUtil.show(StringName.payNotSupport);
           }
         })
         .catchError((error) {
+          LoadingDialog.hide();
           if (error is ServerErrorException) {
             if (error.code == ErrorCode.payOrderError) {
               refreshStoreData();
@@ -292,7 +301,9 @@ class NewDiscountController extends BaseController
           }
         })
         .whenComplete(() {
-          LoadingDialog.hide();
+          if (PlatformUtil.isAndroid) {
+            LoadingDialog.hide();
+          }
         });
   }
 
@@ -377,6 +388,32 @@ class NewDiscountController extends BaseController
     );
   }
 
+  Future<void> _onApplePay(
+    String outTradeNo,
+    String appAccountToken,
+    PayWayInfo payWayInfo,
+    GoodsInfo goodsInfo,
+  ) async {
+    final result = await ApplePay().purchase(
+      productId: goodsInfo.appleGoodsId,
+      appAccountToken: appAccountToken,
+    );
+    if (result["success"] == true) {
+      var receipt = result['receipt'];
+      print('购买成功: ${result['receipt']}');
+      checkPaymentStatus(
+        outTradeNo,
+        payWayInfo,
+        goodsInfo,
+        receiptData: receipt,
+      );
+    } else {
+      LoadingDialog.hide();
+      ToastUtil.show("支付失败,请稍后重试");
+      print('购买失败: ${result['error']}');
+    }
+  }
+
   void checkPaymentStatus(
     String orderNo,
     PayWayInfo payWayInfo,
@@ -511,19 +548,45 @@ class NewDiscountController extends BaseController
 
   @override
   void onPaymentSuccess(
-    String orderNo,
-    PayWayInfo payWayInfo,
-    GoodsInfo goodsInfo,
-  ) {
-    LoadingDialog.hide();
-    AtmobLog.d(tag, 'onPaymentSuccess: $orderNo');
-    Get.back();
-    PaymentSuccessDialog.show(
-      goodsInfo: goodsInfo,
-      btnConfirm: () {
-        AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
-      },
-    );
+      String orderNo,
+      PayWayInfo payWayInfo,
+      GoodsInfo goodsInfo,
+      ) {
+
+    if (PlatformUtil.isIOS) {
+      accountRepository.refreshUserInfo();
+      // 300ms后关闭弹窗
+      Future.delayed(Duration(seconds: 2), () {
+        LoadingDialog.hide();
+        AtmobLog.d(tag, 'onPaymentSuccess: $orderNo');
+        Get.back();
+        PaymentSuccessDialog.show(
+          goodsInfo: goodsInfo,
+          btnConfirm: () {
+            AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
+          },
+        );
+      });
+    } else {
+      LoadingDialog.hide();
+      AtmobLog.d(tag, 'onPaymentSuccess: $orderNo');
+      Get.back();
+      PaymentSuccessDialog.show(
+        goodsInfo: goodsInfo,
+        btnConfirm: () {
+          AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
+        },
+      );
+    }
+  }
+
+  @override
+  void onPaymentError(Error error) {
+    // TODO: implement onPaymentError
+    if (PlatformUtil.isIOS) {
+      LoadingDialog.hide();
+      ErrorHandler.toastError(error);
+    }
   }
 
   /// 恢复订阅
@@ -539,7 +602,7 @@ class NewDiscountController extends BaseController
 
     Future.delayed(const Duration(seconds: 20), () {
       CustomLoadingDialog.hide();
-      ToastUtil.show("Restore record not found");
+      ToastUtil.show("没有发现可恢复的记录");
     });
 
     final result = await ApplePay().restore();
@@ -550,7 +613,7 @@ class NewDiscountController extends BaseController
       checkRestoreStatus(receipt);
     } else {
       CustomLoadingDialog.hide();
-      ToastUtil.show("Pay Error");
+      ToastUtil.show("恢复失败");
       print('恢复失败: ${result['error']}');
     }
 
@@ -573,15 +636,18 @@ class NewDiscountController extends BaseController
     int payPlatform = paymentWay.payPlatform;
     int payMethod = paymentWay.payMethod;
     // var code = await storeRepository.resume(payPlatform, payMethod, receiptData);
-    storeRepository.subscribeResume(payPlatform, payMethod, receiptData).then((data) {
-      CustomLoadingDialog.hide();
-      ToastUtil.show("Restore success");
-      accountRepository.getUserInfo();
-      Get.back();
-    }).catchError((error) {
-      CustomLoadingDialog.hide();
-      ToastUtil.show("Restore fail");
-    });
+    storeRepository
+        .subscribeResume(payPlatform, payMethod, receiptData)
+        .then((data) {
+          CustomLoadingDialog.hide();
+          ToastUtil.show("恢复成功");
+          accountRepository.getUserInfo();
+          Get.back();
+        })
+        .catchError((error) {
+          CustomLoadingDialog.hide();
+          ToastUtil.show("恢复失败");
+        });
 
     // if (code == 0) {
     //   CustomLoadingDialog.hide();

+ 47 - 14
lib/module/store/new_discount/new_discount_page.dart

@@ -720,20 +720,53 @@ class NewDiscountPage extends BasePage<NewDiscountController> {
           );
         }),
         Text.rich(
-          TextSpan(
-            children: [
-              TextSpan(
-                text: StringName.textSpanIHaveReadAndAgree,
-                style: TextStyle(
-                  color: Colors.black.withAlpha(153),
-                  fontSize: 10.sp,
-                  fontWeight: FontWeight.w400,
-                ),
-              ),
-              ClickTextSpan(
-                text: StringName.textSpanPrivacyPolicy,
-                url: WebUrl.privacyPolicy,
-              ),
+          PlatformUtil.isIOS
+              ? TextSpan(
+                children: [
+                  TextSpan(
+                    text: StringName.textSpanIHaveReadAndAgree,
+                    style: TextStyle(
+                      color: Colors.black.withAlpha(153),
+                      fontSize: 10.sp,
+                      fontWeight: FontWeight.w400,
+                    ),
+                  ),
+                  ClickTextSpan(
+                    text: StringName.textSpanPrivacyPolicy,
+                    url: WebUrl.privacyPolicy,
+                  ),
+                  ClickTextSpan(
+                    text: StringName.textSpanServiceTerms,
+                    url: WebUrl.serviceAgreement,
+                  ),
+                  TextSpan(
+                    text: StringName.textSpanAnd,
+                    style: TextStyle(
+                      color: Colors.black.withAlpha(153),
+                      fontSize: 10.sp,
+                      fontWeight: FontWeight.w400,
+                    ),
+                  ),
+                  ClickTextSpan(
+                    text: StringName.textSpanMemberAgreement,
+                    url: WebUrl.subscribeAgreement,
+                  ),
+                ],
+              )
+              : TextSpan(
+                children: [
+                  TextSpan(
+                    text: StringName.textSpanIHaveReadAndAgree,
+                    style: TextStyle(
+                      color: Colors.black.withAlpha(153),
+                      fontSize: 10.sp,
+                      fontWeight: FontWeight.w400,
+                    ),
+                  ),
+                  ClickTextSpan(
+                    text: StringName.textSpanPrivacyPolicy,
+                    url: WebUrl.privacyPolicy,
+                  ),
 
               ClickTextSpan(
                 text: StringName.textSpanServiceTerms,

+ 79 - 20
lib/module/store/store_controller.dart

@@ -24,6 +24,7 @@ import 'package:keyboard/module/store/ticket/discount_ticket_dialog.dart';
 import 'package:keyboard/module/store/util/store_goods_countdown_manager.dart';
 import 'package:keyboard/utils/async_util.dart';
 import 'package:keyboard/utils/atmob_log.dart';
+import 'package:keyboard/widget/platform_util.dart';
 import 'package:lottie/lottie.dart';
 
 import '../../data/api/response/user_info_response.dart';
@@ -282,11 +283,19 @@ class StoreController extends BaseController implements PaymentStatusCallback {
               buyPayWay,
               buyGoods,
             );
+          } else if (payWayType == PayWayType.paymentWayApple) {
+            _onApplePay(
+              response.outTradeNo,
+              response.appAccountToken ?? "",
+              buyPayWay,
+              buyGoods,
+            );
           } else {
             ToastUtil.show(StringName.payNotSupport);
           }
         })
         .catchError((error) {
+          LoadingDialog.hide();
           if (error is ServerErrorException) {
             if (error.code == ErrorCode.payOrderError) {
               refreshStoreData();
@@ -304,7 +313,9 @@ class StoreController extends BaseController implements PaymentStatusCallback {
           }
         })
         .whenComplete(() {
-          LoadingDialog.hide();
+          if (PlatformUtil.isAndroid) {
+            LoadingDialog.hide();
+          }
         });
   }
 
@@ -389,6 +400,26 @@ class StoreController extends BaseController implements PaymentStatusCallback {
     );
   }
 
+  Future<void> _onApplePay(
+      String outTradeNo,
+      String appAccountToken,
+      PayWayInfo payWayInfo,
+      GoodsInfo goodsInfo
+      ) async {
+    final result = await ApplePay().purchase(
+        productId: goodsInfo.appleGoodsId,
+        appAccountToken: appAccountToken);
+    if (result["success"] == true) {
+      var receipt = result['receipt'];
+      print('购买成功: ${result['receipt']}');
+      checkPaymentStatus(outTradeNo, payWayInfo, goodsInfo, receiptData: receipt);
+    } else {
+      LoadingDialog.hide();
+      ToastUtil.show("支付失败,请稍后重试");
+      print('购买失败: ${result['error']}');
+    }
+  }
+
   void checkPaymentStatus(
     String orderNo,
     PayWayInfo payWayInfo,
@@ -504,14 +535,39 @@ class StoreController extends BaseController implements PaymentStatusCallback {
     PayWayInfo payWayInfo,
     GoodsInfo goodsInfo,
   ) {
-    LoadingDialog.hide();
-    Get.back();
-    PaymentSuccessDialog.show(
-      goodsInfo: goodsInfo,
-      btnConfirm: () {
-        AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
-      },
-    );
+
+    if (PlatformUtil.isIOS) {
+      accountRepository.refreshUserInfo();
+      // 300ms后关闭弹窗
+      Future.delayed(Duration(seconds: 2), () {
+        LoadingDialog.hide();
+        Get.back();
+        PaymentSuccessDialog.show(
+          goodsInfo: goodsInfo,
+          btnConfirm: () {
+            AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
+          },
+        );
+      });
+    } else {
+      LoadingDialog.hide();
+      Get.back();
+      PaymentSuccessDialog.show(
+        goodsInfo: goodsInfo,
+        btnConfirm: () {
+          AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}');
+        },
+      );
+    }
+  }
+
+  @override
+  void onPaymentError(Error error) {
+    // TODO: implement onPaymentError
+    if (PlatformUtil.isIOS) {
+      LoadingDialog.hide();
+      ErrorHandler.toastError(error);
+    }
   }
 
   String getUserName() {
@@ -535,7 +591,7 @@ class StoreController extends BaseController implements PaymentStatusCallback {
 
     Future.delayed(const Duration(seconds: 20), () {
       CustomLoadingDialog.hide();
-      ToastUtil.show("Restore record not found");
+      ToastUtil.show("没有发现可恢复的记录");
     });
 
     final result = await ApplePay().restore();
@@ -546,7 +602,7 @@ class StoreController extends BaseController implements PaymentStatusCallback {
       checkRestoreStatus(receipt);
     } else {
       CustomLoadingDialog.hide();
-      ToastUtil.show("Pay Error");
+      ToastUtil.show("恢复失败");
       print('恢复失败: ${result['error']}');
     }
 
@@ -569,15 +625,18 @@ class StoreController extends BaseController implements PaymentStatusCallback {
     int payPlatform = paymentWay.payPlatform;
     int payMethod = paymentWay.payMethod;
     // var code = await storeRepository.resume(payPlatform, payMethod, receiptData);
-    storeRepository.subscribeResume(payPlatform, payMethod, receiptData).then((data) {
-      CustomLoadingDialog.hide();
-      ToastUtil.show("Restore success");
-      accountRepository.getUserInfo();
-      Get.back();
-    }).catchError((error) {
-      CustomLoadingDialog.hide();
-      ToastUtil.show("Restore fail");
-    });
+    storeRepository
+        .subscribeResume(payPlatform, payMethod, receiptData)
+        .then((data) {
+          CustomLoadingDialog.hide();
+          ToastUtil.show("恢复成功");
+          accountRepository.getUserInfo();
+          Get.back();
+        })
+        .catchError((error) {
+          CustomLoadingDialog.hide();
+          ToastUtil.show("恢复失败");
+        });
 
     // if (code == 0) {
     //   CustomLoadingDialog.hide();

+ 68 - 32
lib/module/store/store_page.dart

@@ -46,7 +46,7 @@ class StorePage extends BasePage<StoreController> {
           // iOS平台的隐私协议和服务条款
           _buildLastBottomCorner(
             child: Container(
-              margin: EdgeInsets.only(left: 16.w),
+              // margin: EdgeInsets.only(left: 16.w),
               child: _buildPrivacy(
                 privacyColor: Color(0x99673300),
                 mainAxisAlignment: MainAxisAlignment.start,
@@ -701,7 +701,7 @@ class StorePage extends BasePage<StoreController> {
     return Container(
       alignment: Alignment.centerLeft,
       margin: EdgeInsets.symmetric(horizontal: 16.w),
-      padding: EdgeInsets.symmetric(horizontal: 16.w),
+      padding: EdgeInsets.symmetric(horizontal: PlatformUtil.isIOS ? 4.w : 16.w),
       width: double.infinity,
       height: 36.h,
       decoration: ShapeDecoration(
@@ -1120,7 +1120,7 @@ class StorePage extends BasePage<StoreController> {
               controller.isAgree.value = !controller.isAgree.value;
             },
             child: Container(
-              margin: EdgeInsets.only(top: 2.h),
+              margin: EdgeInsets.only(top: PlatformUtil.isIOS ? 0.h : 2.h),
               padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 5.w),
               child: Image(
                 image:
@@ -1136,38 +1136,74 @@ class StorePage extends BasePage<StoreController> {
         Transform.translate(
           offset: Offset(-2.w, 0),
           child: Text.rich(
-            TextSpan(
-              children: [
-                TextSpan(
-                  text: StringName.textSpanIHaveReadAndAgree,
-                  style: TextStyle(
-                    color: Colors.black.withAlpha(153),
-                    fontSize: 10.sp,
-                    fontWeight: FontWeight.w400,
-                  ),
-                ),
-                ClickTextSpan(
-                  text: StringName.textSpanPrivacyPolicy,
-                  url: WebUrl.privacyPolicy,
-                  color: privacyColor,
-                ),
+            PlatformUtil.isIOS
+                ? TextSpan(
+                  children: [
+                    TextSpan(
+                      text: StringName.textSpanIHaveReadAndAgree,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    ClickTextSpan(
+                      text: StringName.textSpanPrivacyPolicy,
+                      url: WebUrl.privacyPolicy,
+                      color: privacyColor,
+                    ),
+                    ClickTextSpan(
+                      text: StringName.textSpanServiceTerms,
+                      url: WebUrl.serviceAgreement,
+                      color: privacyColor,
+                    ),
+                    TextSpan(
+                      text: StringName.textSpanAnd,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    ClickTextSpan(
+                      text: StringName.textSpanMemberAgreement,
+                      url: WebUrl.subscribeAgreement,
+                      color: privacyColor,
+                    ),
+                  ],
+                )
+                : TextSpan(
+                  children: [
+                    TextSpan(
+                      text: StringName.textSpanIHaveReadAndAgree,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
+                    ClickTextSpan(
+                      text: StringName.textSpanPrivacyPolicy,
+                      url: WebUrl.privacyPolicy,
+                      color: privacyColor,
+                    ),
 
-                TextSpan(
-                  text: StringName.textSpanAnd,
-                  style: TextStyle(
-                    color: Colors.black.withAlpha(153),
-                    fontSize: 10.sp,
-                    fontWeight: FontWeight.w400,
-                  ),
-                ),
+                    TextSpan(
+                      text: StringName.textSpanAnd,
+                      style: TextStyle(
+                        color: Colors.black.withAlpha(153),
+                        fontSize: 10.sp,
+                        fontWeight: FontWeight.w400,
+                      ),
+                    ),
 
-                ClickTextSpan(
-                  text: StringName.textSpanServiceTerms,
-                  url: WebUrl.serviceAgreement,
-                  color: privacyColor,
+                    ClickTextSpan(
+                      text: StringName.textSpanServiceTerms,
+                      url: WebUrl.serviceAgreement,
+                      color: privacyColor,
+                    ),
+                  ],
                 ),
-              ],
-            ),
           ),
         ),
       ],

+ 2 - 0
lib/resource/string.gen.dart

@@ -133,6 +133,7 @@ class StringName {
   static final String textSpanPrivacyPolicy = 'text_span_privacy_policy'.tr; // 《隐私政策》
   static final String textSpanServiceTerms = 'text_span_service_terms'.tr; // 《服务条款》
   static final String textSpanUserAgreement = 'text_span_user_agreement'.tr; // 《用户协议》
+  static final String textSpanMemberAgreement = 'text_span_member_agreement'.tr; // 《会员服务协议》
   static final String recoverSubscribe = 'recover_subscribe'.tr; // 恢复订阅
   static final String recoverSubscribeTitle = 'recover_subscribe_title'.tr; // 已恢复订阅
   static final String recoverSubscribeConfirm = 'recover_subscribe_confirm'.tr; // 知道了
@@ -495,6 +496,7 @@ class StringMultiSource {
       'text_span_privacy_policy': '《隐私政策》',
       'text_span_service_terms': '《服务条款》',
       'text_span_user_agreement': '《用户协议》',
+      'text_span_member_agreement': '《会员服务协议》',
       'recover_subscribe': '恢复订阅',
       'recover_subscribe_title': '已恢复订阅',
       'recover_subscribe_confirm': '知道了',

+ 13 - 4
lib/utils/default_keyboard_helper.dart

@@ -1,4 +1,7 @@
+import 'dart:io';
+
 import 'package:get/get.dart';
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
 import '../plugins/default_keyboard_monitor.dart';
 import '../plugins/keyboard_android_platform.dart';
 
@@ -17,10 +20,16 @@ class DefaultKeyboardHelper {
     DefaultKeyboardMonitor.registerDefaultKeyboardChangeEvent((isDefault) {
       _handleDefaultKeyboardChange(isDefault);
     });
-    // 马上获取一次数据
-    KeyboardAndroidPlatform.isDefaultKeyboard().then((isDefaultKeyboard) {
-      _handleDefaultKeyboardChange(isDefaultKeyboard);
-    });
+    if (Platform.isAndroid) {
+      // 马上获取一次数据
+      KeyboardAndroidPlatform.isDefaultKeyboard().then((isDefaultKeyboard) {
+        _handleDefaultKeyboardChange(isDefaultKeyboard);
+      });
+    } else {
+      MethodChanelIOSUtil.isDefaultKeyboard().then((isDefaultKeyboard) {
+        _handleDefaultKeyboardChange(isDefaultKeyboard);
+      });
+    }
   }
 
   /// 处理默认键盘切换

+ 6 - 3
lib/utils/floating_window_helper.dart

@@ -1,4 +1,5 @@
 import 'package:get/get.dart';
+import 'package:keyboard/widget/platform_util.dart';
 
 import '../plugins/keyboard_android_platform.dart';
 
@@ -11,9 +12,11 @@ class FloatingWindowHelper {
 
   /// 初始化
   static void init() {
-    KeyboardAndroidPlatform.hasFloatingWindowPermission().then((hasPermission) {
-      hasFloatingWindowPermission.value = hasPermission;
-    });
+    if (PlatformUtil.isAndroid) {
+      KeyboardAndroidPlatform.hasFloatingWindowPermission().then((hasPermission) {
+        hasFloatingWindowPermission.value = hasPermission;
+      });
+    }
   }
 
   /// 更新权限状态

+ 30 - 0
lib/utils/keyboard_detect_ios_util.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/services.dart';
+import 'dart:async';
+
+import 'package:keyboard/utils/default_keyboard_helper.dart';
+
+class KeyboardDetectIOSUtil {
+
+  static const EventChannel _eventChannel = EventChannel('keyboard_ios_events');
+
+  // 流控制器
+  static final StreamController<bool> _keyboardStreamController = StreamController<bool>.broadcast();
+
+  // 公开的流,用于监听键盘变化
+  static Stream<bool> get keyboardStream => _keyboardStreamController.stream;
+
+  // 初始化
+  static void initialize() {
+    // 监听来自原生代码的事件
+    _eventChannel.receiveBroadcastStream().listen((dynamic event) {
+      final bool isCustomKeyboard = event as bool;
+      _keyboardStreamController.add(isCustomKeyboard);
+      DefaultKeyboardHelper.isDefaultKeyboard.value = isCustomKeyboard;
+    });
+  }
+
+  // 释放资源
+  static void dispose() {
+    _keyboardStreamController.close();
+  }
+}

+ 5 - 1
lib/utils/keyboard_tutorial_util.dart

@@ -1,6 +1,7 @@
 import 'dart:io';
 
 import 'package:keyboard/data/consts/constants.dart';
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
 import 'package:keyboard/utils/mmkv_util.dart';
 
 import '../module/keyboard_guide/keyboard_guide_page.dart';
@@ -58,7 +59,10 @@ class KeyboardTutorialUtil {
       bool hasIsUseKeyboard =
       await KeyboardAndroidPlatform.isTargetKeyboardEnabled();
       return hasFloatingWindowPermission && hasIsUseKeyboard;
+    } else {
+      // 是否启用了键盘
+      bool hasIsUseKeyboard = await MethodChanelIOSUtil.isKeyboardAdded();
+      return hasIsUseKeyboard;
     }
-    return true;
   }
 }

+ 36 - 0
lib/utils/method_chanel_ios_util.dart

@@ -6,6 +6,7 @@ import 'package:keyboard/module/character_custom/character_custom_page.dart';
 import 'package:keyboard/module/login/login_page.dart';
 import 'package:keyboard/module/main/main_page.dart';
 import 'package:keyboard/module/store/store_page.dart';
+import 'package:keyboard/utils/default_keyboard_helper.dart';
 
 import '../module/intimacy_scale/intimacy_scale_page.dart';
 
@@ -18,6 +19,8 @@ class MethodChanelIOSUtil {
 
   static void initialize() {
     _channel.setMethodCallHandler(_handleMethod);
+    // 默认键盘切换监听器
+    DefaultKeyboardHelper.init();
   }
 
   static Future<dynamic> _handleMethod(MethodCall call) async {
@@ -37,6 +40,8 @@ class MethodChanelIOSUtil {
       case 'navigateToIntimacy':
         IntimacyScalePage.start();
         break;
+      case 'isKeyboardAdd':
+        break;
       default:
         throw PlatformException(
           code: 'Unimplemented',
@@ -76,4 +81,35 @@ class MethodChanelIOSUtil {
       _channel.invokeMethod('clearAuthToken');
     }
   }
+
+  // 检查键盘是否已添加
+  static Future<bool> isDefaultKeyboard() async {
+    try {
+      // 调用原生方法并等待返回结果
+      final bool result = await _channel.invokeMethod('isDefaultKeyboard');
+      return result;
+    } on PlatformException catch (e) {
+      print('检查键盘是否为默认键盘: ${e.message}');
+      return false; // 发生错误时返回 false
+    }
+  }
+
+  // 检查键盘是否已添加
+  static Future<bool> isKeyboardAdded() async {
+    try {
+      // 调用原生方法并等待返回结果
+      final bool result = await _channel.invokeMethod('isKeyboardAdded');
+      return result;
+    } on PlatformException catch (e) {
+      print('检查键盘是否添加失败: ${e.message}');
+      return false; // 发生错误时返回 false
+    }
+  }
+
+  static Future<void> openKeyboardGuide() async {
+    // 通知iOS键盘扩展
+    if (Platform.isIOS) {
+      _channel.invokeMethod('openKeyboardGuide');
+    }
+  }
 }

+ 4 - 0
lib/utils/payment_status_manager.dart

@@ -62,6 +62,8 @@ class PaymentStatusManager {
             payWayInfo.payMethod);
       }).catchError((error) async {
         await _lock.synchronized(() {
+          callbackMap[orderNo]
+              ?.onPaymentError(error);
           callbackMap.remove(orderNo);
         });
         debugPrint('支付失败: orderNo = $orderNo, error = $error');
@@ -129,4 +131,6 @@ class PaymentStatusException implements Exception {
 abstract class PaymentStatusCallback {
   void onPaymentSuccess(
       String orderNo, PayWayInfo payWayInfo, GoodsInfo goodsInfo);
+
+  void onPaymentError(Error error);
 }

+ 1 - 1
lib/utils/url_launcher_util.dart

@@ -10,7 +10,7 @@ class UrlLauncherUtil {
 
   /// 打开iOS系统键盘设置页
   static openIosSystemKeyboardPage() {
-    const url = 'App-Prefs:root=General&path=Keyboard';
+    const url = 'app-settings:';
     return openUrl(url);
   }
 

+ 1 - 0
plugins/flutter_umeng/ios/Classes/FlutterUmengPlugin.swift

@@ -13,6 +13,7 @@ public class FlutterUmengPlugin: NSObject, FlutterPlugin {
     switch call.method {
     case "initCommon":
       initialize()
+      result(true)
     case "setPolicyGrantResult":
       // initialize()
       break