Skip to Content
SDKSDKsXenseAR Native SDKFlutterHướng dẫn cài đặt XenseAR SDK cho Flutter iOS

Hướng dẫn cài đặt XenseAR SDK cho Flutter iOS

  1. Copy XenseAR SDK vào thư mục <flutter root>/ios/unityLibrary/.
  1. Thêm package vào <flutter root>/pubspec.yaml.
  • Thêm các package cần thiết:
dependencies: # Thêm hai package này vào danh sách flutter_embed_unity: ^1.4.0
  • Sau khi đã thêm, thực hiện download qua terminal:
flutter pub get
  1. Mở file xcworkspace tại <your flutter project>/ios/Runner.xcworkspace.
  • Việc mở file xcworkspace mà không phải file xcproj là cực kỳ quan trọng. File xcworkspace được dùng để kết hợp nhiều projects (Bao gồm Project Runner của Flutter và XenseAR Library).
  1. Thêm XenseAR vào workspace.
  • Từ trên Menu chọn File -> Add Files to "Runner"....
  • Chọn xcodeproj của XenseAR Library, thường được đặt tại <your flutter project>/ios/UnityLibrary/Unity-Iphone.xcodeproj.
  • Đảm bảo XenseAR Library nằm cùng cấp với Runner.
  1. Chỉnh sửa file <your flutter app>/ios/Podfile:
  • XenseAR Library cần hệ điều hành tối thiểu iOS 13+ để hoạt động
# XenseAR Library requires iOS 13+ platform :ios, '13.0'
  • Nếu không thấy file Podfile, thực hiện lại bước cài lại flutter:
flutter clean flutter pub get
  • Cài đặt pod:
cd ios & pod install
  1. Copy Framework
  • Chọn project Runner
  • Đến phần Target -> Runner -> General -> Frameworks, Libraries, and Embedded Content
  • Copy framework thư mục Unity-iPhone/Frameworks/Silver Tau/ vào Frameworks, Libraries, and Embedded Content
  • Chọn chế độ EmbedEmbed & Sign
  1. Bổ sung quyền trong Info.plist
  • Chọn project Runner
  • Đến phần Target -> Runner -> Info -> Custom iOS Target Properties
  • Bổ sung hai key:
    • Privacy - Camera Usage Description
    • Privacy - Microphone Usage Description
  1. Sửa lỗi đen màn hình
  • Do xung đột giữa cách flutter và một package trong XenseAR Library hoạt động, cần thêm một vài đoạn code vào file ios/Runner/AppDelegate.swift
    • Nhận tín hiệu có thay đổi window trong application
    • Đưa webview vào UnityView
import Flutter import UIKit @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) // 1️⃣ Observe when new windows appear or become active NotificationCenter.default.addObserver( self, selector: #selector(onWindowChange), name: UIWindow.didBecomeVisibleNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(onWindowChange), name: UIWindow.didBecomeKeyNotification, object: nil ) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } // 🔁 Called when any window becomes visible or key @objc func onWindowChange(_ notification: Notification) { print("🪟 Window change detected — re-checking WKWebView position.") // Delay slightly to let UIKit attach new layers DispatchQueue.main.async { self.moveWKWebViewToFrontmostWindow() } } func moveWKWebViewToFrontmostWindow() { // 1️⃣ Collect all visible windows from active scenes let allWindows = UIApplication.shared.connectedScenes .compactMap { ($0 as? UIWindowScene)?.windows } .flatMap { $0 } .filter { !$0.isHidden } // ignore hidden windows guard !allWindows.isEmpty else { print("❌ No visible windows found.") return } // 2️⃣ Locate the UnityView inside any visible window guard let unityView = allWindows .compactMap({ findSubview(in: $0, nameContains: "UnityView") }) .first else { print("⚠️ UnityView not found in any window.") return } // 3️⃣ Locate the Vuplex fullscreen WKWebView guard let wkWebView = allWindows .compactMap({ findSubview(in: $0, nameContains: "VXFullScreenWKWebView") }) .first else { print("⚠️ WKWebView not found in any window.") return } // 4️⃣ If WKWebView is already in the UnityView, no action needed if wkWebView.superview === unityView { print("✅ WKWebView is already inside the UnityView.") return } // 5️⃣ Move WKWebView from its current parent to UnityView let previousSuperview = wkWebView.superview wkWebView.removeFromSuperview() unityView.addSubview(wkWebView) // 6️⃣ Make WKWebView fill the UnityView entirely wkWebView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ wkWebView.leadingAnchor.constraint(equalTo: unityView.leadingAnchor), wkWebView.trailingAnchor.constraint(equalTo: unityView.trailingAnchor), wkWebView.topAnchor.constraint(equalTo: unityView.topAnchor), wkWebView.bottomAnchor.constraint(equalTo: unityView.bottomAnchor), ]) // 7️⃣ Hide and clean up the old window if it’s different from UnityView's window if let prevWindow = previousSuperview?.window, prevWindow !== unityView.window { prevWindow.isHidden = true } // 8️⃣ Remove window observers (no longer needed) NotificationCenter.default.removeObserver( self, name: UIWindow.didBecomeKeyNotification, object: nil ) NotificationCenter.default.removeObserver( self, name: UIWindow.didBecomeVisibleNotification, object: nil ) print("✅ WKWebView moved from \(String(describing: previousSuperview)) to UnityView \(unityView).") } private func findSubview(in view: UIView, nameContains keyword: String) -> UIView? { if String(describing: type(of: view)).contains(keyword) { return view } for subview in view.subviews { if let match = findSubview(in: subview, nameContains: keyword) { return match } } return nil } }
Last updated on