Merge commit '23503592f91f071d2a771c0a7fbcd3c48bef01eb'
This commit is contained in:
commit
c6026f57dc
@ -16,10 +16,7 @@ void main() {
|
|||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
||||||
final FlutterCocosView plugin = FlutterCocosView();
|
|
||||||
final String? version = await plugin.getPlatformVersion();
|
|
||||||
// The version string depends on the host platform running the test, so
|
|
||||||
// just assert that some non-empty string is returned.
|
|
||||||
expect(version?.isNotEmpty, true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -109,18 +109,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -149,18 +149,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.8.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.12.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -173,10 +173,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.5"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -222,6 +222,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -250,10 +258,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -266,10 +274,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.5"
|
version: "14.2.1"
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -279,5 +287,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.4 <4.0.0"
|
dart: ">=3.4.4 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.18.0-18.0.pre.54"
|
||||||
|
@ -5,7 +5,7 @@ description: "Demonstrates how to use the flutter_cocos_view plugin."
|
|||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.4.4
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
281
ios/Classes/CocosPlayerUtils.swift
Normal file
281
ios/Classes/CocosPlayerUtils.swift
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
//
|
||||||
|
// CocosPlayerUtils.swift
|
||||||
|
// flutter_cocos_widget
|
||||||
|
//
|
||||||
|
// Created by Rex Raphael on 30/01/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
private var cocos_warmed_up = false
|
||||||
|
// Hack to work around iOS SDK 4.3 linker problem
|
||||||
|
// we need at least one __TEXT, __const section entry in main application .o files
|
||||||
|
// to get this section emitted at right time and so avoid LC_ENCRYPTION_INFO size miscalculation
|
||||||
|
private let constsection = 0
|
||||||
|
|
||||||
|
// keep arg for cocos init from non main
|
||||||
|
var gArgc: Int32 = 0
|
||||||
|
var gArgv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? = nil
|
||||||
|
var appLaunchOpts: [UIApplication.LaunchOptionsKey: Any]? = [:]
|
||||||
|
|
||||||
|
/***********************************PLUGIN_ENTRY STARTS**************************************/
|
||||||
|
public func InitCocosIntegration(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) {
|
||||||
|
gArgc = argc
|
||||||
|
gArgv = argv
|
||||||
|
}
|
||||||
|
|
||||||
|
public func InitCocosIntegrationWithOptions(
|
||||||
|
argc: Int32,
|
||||||
|
argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?,
|
||||||
|
_ launchingOptions: [UIApplication.LaunchOptionsKey: Any]?) {
|
||||||
|
gArgc = argc
|
||||||
|
gArgv = argv
|
||||||
|
appLaunchOpts = launchingOptions
|
||||||
|
}
|
||||||
|
/***********************************PLUGIN_ENTRY END**************************************/
|
||||||
|
|
||||||
|
// Load cocos framework for fisrt run
|
||||||
|
func CocosFrameworkLoad() -> CocosFramework? {
|
||||||
|
var bundlePath: String? = nil
|
||||||
|
bundlePath = Bundle.main.bundlePath
|
||||||
|
bundlePath = (bundlePath ?? "") + "/Frameworks/CocosFramework.framework"
|
||||||
|
|
||||||
|
let bundle = Bundle(path: bundlePath ?? "")
|
||||||
|
if bundle?.isLoaded == false {
|
||||||
|
bundle?.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
return bundle?.principalClass?.getInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************** GLOBAL FUNCS & VARS START**************************************/
|
||||||
|
public var globalControllers: Array<FLTCocosWidgetController> = [FLTCocosWidgetController]()
|
||||||
|
|
||||||
|
private var cocosPlayerUtils: CocosPlayerUtils? = nil
|
||||||
|
func GetCocosPlayerUtils() -> CocosPlayerUtils {
|
||||||
|
|
||||||
|
if cocosPlayerUtils == nil {
|
||||||
|
cocosPlayerUtils = CocosPlayerUtils()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cocosPlayerUtils ?? CocosPlayerUtils()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************** GLOBAL FUNCS & VARS END****************************************/
|
||||||
|
|
||||||
|
var controller: CocosAppController?
|
||||||
|
var sharedApplication: UIApplication?
|
||||||
|
|
||||||
|
@objc protocol CocosEventListener: AnyObject {
|
||||||
|
|
||||||
|
func onReceiveMessage(_ message: UnsafePointer<Int8>?)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public class CocosPlayerUtils: UIResponder, UIApplicationDelegate, CocosFrameworkListener {
|
||||||
|
var ufw: CocosFramework!
|
||||||
|
private var _isCocosPaused = false
|
||||||
|
private var _isCocosReady = false
|
||||||
|
private var _isCocosLoaded = false
|
||||||
|
|
||||||
|
func initCocos() {
|
||||||
|
if (self.cocosIsInitiallized()) {
|
||||||
|
self.ufw?.showCocosWindow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ufw = CocosFrameworkLoad()
|
||||||
|
|
||||||
|
self.ufw?.setDataBundleId("com.cocos3d.framework")
|
||||||
|
|
||||||
|
registerCocosListener()
|
||||||
|
self.ufw?.runEmbedded(withArgc: gArgc, argv: gArgv, appLaunchOpts: appLaunchOpts)
|
||||||
|
|
||||||
|
if self.ufw?.appController() != nil {
|
||||||
|
controller = self.ufw?.appController()
|
||||||
|
controller?.cocosMessageHandler = self.cocosMessageHandlers
|
||||||
|
controller?.cocosSceneLoadedHandler = self.cocosSceneLoadedHandlers
|
||||||
|
self.ufw?.appController()?.window?.windowLevel = UIWindow.Level(UIWindow.Level.normal.rawValue - 1)
|
||||||
|
}
|
||||||
|
_isCocosLoaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if cocos is initiallized
|
||||||
|
func cocosIsInitiallized() -> Bool {
|
||||||
|
if self.ufw != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new cocos player
|
||||||
|
func createPlayer(completed: @escaping (_ view: UIView?) -> Void) {
|
||||||
|
if self.cocosIsInitiallized() && self._isCocosReady {
|
||||||
|
completed(controller?.rootView)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(forName: NSNotification.Name("CocosReady"), object: nil, queue: OperationQueue.main, using: { note in
|
||||||
|
self._isCocosReady = true
|
||||||
|
completed(controller?.rootView)
|
||||||
|
})
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
// if (sharedApplication == nil) {
|
||||||
|
// sharedApplication = UIApplication.shared
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Always keep Flutter window on top
|
||||||
|
// let flutterUIWindow = sharedApplication?.keyWindow
|
||||||
|
// flutterUIWindow?.windowLevel = UIWindow.Level(UIWindow.Level.normal.rawValue + 1) // Always keep Flutter window in top
|
||||||
|
// sharedApplication?.keyWindow?.windowLevel = UIWindow.Level(UIWindow.Level.normal.rawValue + 1)
|
||||||
|
|
||||||
|
self.initCocos()
|
||||||
|
|
||||||
|
cocos_warmed_up = true
|
||||||
|
self._isCocosReady = true
|
||||||
|
self._isCocosLoaded = true
|
||||||
|
|
||||||
|
self.listenAppState()
|
||||||
|
|
||||||
|
completed(controller?.rootView)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerCocosListener() {
|
||||||
|
if self.cocosIsInitiallized() {
|
||||||
|
self.ufw?.register(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unregisterCocosListener() {
|
||||||
|
if self.cocosIsInitiallized() {
|
||||||
|
self.ufw?.unregisterFrameworkListener(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
public func cocosDidUnload(_ notification: Notification!) {
|
||||||
|
unregisterCocosListener()
|
||||||
|
self.ufw = nil
|
||||||
|
self._isCocosReady = false
|
||||||
|
self._isCocosLoaded = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleAppStateDidChange(notification: Notification?) {
|
||||||
|
if !self._isCocosReady {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let cocosAppController = self.ufw?.appController() as? CocosAppController
|
||||||
|
let application = UIApplication.shared
|
||||||
|
|
||||||
|
if notification?.name == UIApplication.willResignActiveNotification {
|
||||||
|
cocosAppController?.applicationWillResignActive(application)
|
||||||
|
} else if notification?.name == UIApplication.didEnterBackgroundNotification {
|
||||||
|
cocosAppController?.applicationDidEnterBackground(application)
|
||||||
|
} else if notification?.name == UIApplication.willEnterForegroundNotification {
|
||||||
|
cocosAppController?.applicationWillEnterForeground(application)
|
||||||
|
} else if notification?.name == UIApplication.didBecomeActiveNotification {
|
||||||
|
cocosAppController?.applicationDidBecomeActive(application)
|
||||||
|
} else if notification?.name == UIApplication.willTerminateNotification {
|
||||||
|
cocosAppController?.applicationWillTerminate(application)
|
||||||
|
} else if notification?.name == UIApplication.didReceiveMemoryWarningNotification {
|
||||||
|
cocosAppController?.applicationDidReceiveMemoryWarning(application)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Listener for app lifecycle eventa
|
||||||
|
func listenAppState() {
|
||||||
|
for name in [
|
||||||
|
UIApplication.didBecomeActiveNotification,
|
||||||
|
UIApplication.didEnterBackgroundNotification,
|
||||||
|
UIApplication.willTerminateNotification,
|
||||||
|
UIApplication.willResignActiveNotification,
|
||||||
|
UIApplication.willEnterForegroundNotification,
|
||||||
|
UIApplication.didReceiveMemoryWarningNotification
|
||||||
|
] {
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(self.handleAppStateDidChange),
|
||||||
|
name: name,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pause cocos player
|
||||||
|
func pause() {
|
||||||
|
self.ufw?.pause(true)
|
||||||
|
self._isCocosPaused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume cocos player
|
||||||
|
func resume() {
|
||||||
|
self.ufw?.pause(false)
|
||||||
|
self._isCocosPaused = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unoad cocos player
|
||||||
|
func unload() {
|
||||||
|
self.ufw?.unloadApplication()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCocosLoaded() -> Bool {
|
||||||
|
return _isCocosLoaded
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCocosPaused() -> Bool {
|
||||||
|
return _isCocosPaused
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit cocos player application
|
||||||
|
func quit() {
|
||||||
|
self.ufw?.quitApplication(0)
|
||||||
|
self._isCocosLoaded = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post message to cocos
|
||||||
|
func postMessageToCocos(gameObject: String?, cocosMethodName: String?, cocosMessage: String?) {
|
||||||
|
if self.cocosIsInitiallized() {
|
||||||
|
self.ufw?.sendMessageToGO(withName: gameObject, functionName: cocosMethodName, message: cocosMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle incoming cocos messages looping through all controllers and passing payload to
|
||||||
|
/// the controller handler methods
|
||||||
|
@objc
|
||||||
|
func cocosMessageHandlers(_ message: UnsafePointer<Int8>?) {
|
||||||
|
for c in globalControllers {
|
||||||
|
if let strMsg = message {
|
||||||
|
c.handleMessage(message: String(utf8String: strMsg) ?? "")
|
||||||
|
} else {
|
||||||
|
c.handleMessage(message: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cocosSceneLoadedHandlers(name: UnsafePointer<Int8>?, buildIndex: UnsafePointer<Int32>?, isLoaded: UnsafePointer<Bool>?, isValid: UnsafePointer<Bool>?) {
|
||||||
|
if let sceneName = name,
|
||||||
|
let bIndex = buildIndex,
|
||||||
|
let loaded = isLoaded,
|
||||||
|
let valid = isValid {
|
||||||
|
|
||||||
|
let loadedVal = Bool((Int(bitPattern: loaded) != 0))
|
||||||
|
let validVal = Bool((Int(bitPattern: valid) != 0))
|
||||||
|
|
||||||
|
let addObject: Dictionary<String, Any> = [
|
||||||
|
"name": String(utf8String: sceneName) ?? "",
|
||||||
|
"buildIndex": Int(bitPattern: bIndex),
|
||||||
|
"isLoaded": loadedVal,
|
||||||
|
"isValid": validVal,
|
||||||
|
]
|
||||||
|
|
||||||
|
for c in globalControllers {
|
||||||
|
c.handleSceneChangeEvent(info: addObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
ios/Classes/FLTCocosOptionsSink.swift
Normal file
13
ios/Classes/FLTCocosOptionsSink.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// FLTCocosOptionsSink.swift
|
||||||
|
// flutter_unity_widget
|
||||||
|
//
|
||||||
|
// Created by Rex Raphael on 30/01/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// Defines map UI options writable from Flutter.
|
||||||
|
protocol FLTCocosOptionsSink: AnyObject {
|
||||||
|
func setDisabledUnload(enabled: Bool)
|
||||||
|
}
|
19
ios/Classes/FLTCocosView.swift
Normal file
19
ios/Classes/FLTCocosView.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// FLTCocosView.swift
|
||||||
|
// flutter_unity_widget
|
||||||
|
//
|
||||||
|
// Created by Rex Raphael on 30/01/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
class FLTCocosView: UIView {
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
if (!self.bounds.isEmpty) {
|
||||||
|
GetCocosPlayerUtils().ufw?.appController()?.rootView.frame = self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
ios/Classes/FLTCocosViewFactory.swift
Normal file
30
ios/Classes/FLTCocosViewFactory.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// FLTCocosViewFactory.swift
|
||||||
|
// flutter_unity_widget
|
||||||
|
//
|
||||||
|
// Created by Rex Raphael on 30/01/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class FLTCocosWidgetFactory: NSObject, FlutterPlatformViewFactory {
|
||||||
|
private weak var registrar: FlutterPluginRegistrar?
|
||||||
|
|
||||||
|
init(registrar: NSObjectProtocol & FlutterPluginRegistrar) {
|
||||||
|
super.init()
|
||||||
|
self.registrar = registrar
|
||||||
|
}
|
||||||
|
|
||||||
|
func createArgsCodec() -> (NSObjectProtocol & FlutterMessageCodec) {
|
||||||
|
return FlutterStandardMessageCodec.sharedInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
|
||||||
|
let controller = FLTCocosWidgetController(
|
||||||
|
frame: frame,
|
||||||
|
viewIdentifier: viewId,
|
||||||
|
arguments: args,
|
||||||
|
registrar: registrar!)
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
}
|
182
ios/Classes/FLTCocosWidgetController.swift
Normal file
182
ios/Classes/FLTCocosWidgetController.swift
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
//
|
||||||
|
// FLTCocosViewController.swift
|
||||||
|
// flutter_cocos_widget
|
||||||
|
//
|
||||||
|
// Created by Rex Raphael on 30/01/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CocosFramework
|
||||||
|
|
||||||
|
// Defines cocos controllable from Flutter.
|
||||||
|
public class FLTCocosWidgetController: NSObject, FLTCocosOptionsSink, FlutterPlatformView {
|
||||||
|
private var _rootView: FLTCocosView
|
||||||
|
private var viewId: Int64 = 0
|
||||||
|
private var channel: FlutterMethodChannel?
|
||||||
|
private weak var registrar: (NSObjectProtocol & FlutterPluginRegistrar)?
|
||||||
|
|
||||||
|
private var _disposed = false
|
||||||
|
|
||||||
|
init(
|
||||||
|
frame: CGRect,
|
||||||
|
viewIdentifier viewId: Int64,
|
||||||
|
arguments args: Any?,
|
||||||
|
registrar: NSObjectProtocol & FlutterPluginRegistrar
|
||||||
|
) {
|
||||||
|
self._rootView = FLTCocosView(frame: frame)
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
globalControllers.append(self)
|
||||||
|
|
||||||
|
self.viewId = viewId
|
||||||
|
|
||||||
|
let channelName = String(format: "plugin.xraph.com/cocos_view_%lld", viewId)
|
||||||
|
self.channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
|
||||||
|
|
||||||
|
self.channel?.setMethodCallHandler(self.methodHandler)
|
||||||
|
self.attachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func methodHandler(_ call: FlutterMethodCall, result: FlutterResult) {
|
||||||
|
if call.method == "cocos#dispose" {
|
||||||
|
self.dispose()
|
||||||
|
result(nil)
|
||||||
|
} else {
|
||||||
|
self.reattachView()
|
||||||
|
if call.method == "cocos#isReady" {
|
||||||
|
result(GetCocosPlayerUtils().cocosIsInitiallized())
|
||||||
|
} else if call.method == "cocos#isLoaded" {
|
||||||
|
let _isUnloaded = GetCocosPlayerUtils().isCocosLoaded()
|
||||||
|
result(_isUnloaded)
|
||||||
|
} else if call.method == "cocos#createCocosPlayer" {
|
||||||
|
startCocosIfNeeded()
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#isPaused" {
|
||||||
|
let _isPaused = GetCocosPlayerUtils().isCocosPaused()
|
||||||
|
result(_isPaused)
|
||||||
|
} else if call.method == "cocos#pausePlayer" {
|
||||||
|
GetCocosPlayerUtils().pause()
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#postMessage" {
|
||||||
|
self.postMessage(call: call, result: result)
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#resumePlayer" {
|
||||||
|
GetCocosPlayerUtils().resume()
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#unloadPlayer" {
|
||||||
|
GetCocosPlayerUtils().unload()
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#quitPlayer" {
|
||||||
|
GetCocosPlayerUtils().quit()
|
||||||
|
result(nil)
|
||||||
|
} else if call.method == "cocos#waitForCocos" {
|
||||||
|
result(nil)
|
||||||
|
} else {
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDisabledUnload(enabled: Bool) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func view() -> UIView {
|
||||||
|
return _rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startCocosIfNeeded() {
|
||||||
|
GetCocosPlayerUtils().createPlayer(completed: { [self] (view: UIView?) in
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func attachView() {
|
||||||
|
startCocosIfNeeded()
|
||||||
|
|
||||||
|
let cocosView = GetCocosPlayerUtils().ufw?.appController()?.rootView
|
||||||
|
if let superview = cocosView?.superview {
|
||||||
|
cocosView?.removeFromSuperview()
|
||||||
|
superview.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let cocosView = cocosView {
|
||||||
|
_rootView.addSubview(cocosView)
|
||||||
|
_rootView.layoutIfNeeded()
|
||||||
|
self.channel?.invokeMethod("events#onViewReattached", arguments: "")
|
||||||
|
}
|
||||||
|
GetCocosPlayerUtils().resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func reattachView() {
|
||||||
|
let cocosView = GetCocosPlayerUtils().ufw?.appController()?.rootView
|
||||||
|
let superview = cocosView?.superview
|
||||||
|
if superview != _rootView {
|
||||||
|
attachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
GetCocosPlayerUtils().resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeViewIfNeeded() {
|
||||||
|
if GetCocosPlayerUtils().ufw == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let cocosView = GetCocosPlayerUtils().ufw?.appController()?.rootView
|
||||||
|
if _rootView == cocosView?.superview {
|
||||||
|
if globalControllers.isEmpty {
|
||||||
|
cocosView?.removeFromSuperview()
|
||||||
|
cocosView?.superview?.layoutIfNeeded()
|
||||||
|
} else {
|
||||||
|
globalControllers.last?.reattachView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GetCocosPlayerUtils().resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
if _disposed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
globalControllers.removeAll{ value in
|
||||||
|
return value == self
|
||||||
|
}
|
||||||
|
|
||||||
|
channel?.setMethodCallHandler(nil)
|
||||||
|
removeViewIfNeeded()
|
||||||
|
|
||||||
|
_disposed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles messages from cocos in the current view
|
||||||
|
func handleMessage(message: String) {
|
||||||
|
self.channel?.invokeMethod("events#onCocosMessage", arguments: message)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Handles scene changed event from cocos in the current view
|
||||||
|
func handleSceneChangeEvent(info: Dictionary<String, Any>) {
|
||||||
|
self.channel?.invokeMethod("events#onCocosSceneLoaded", arguments: info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post messages to cocos from flutter
|
||||||
|
func postMessage(call: FlutterMethodCall, result: FlutterResult) {
|
||||||
|
guard let args = call.arguments else {
|
||||||
|
result("iOS could not recognize flutter arguments in method: (postMessage)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let myArgs = args as? [String: Any],
|
||||||
|
let gObj = myArgs["gameObject"] as? String,
|
||||||
|
let method = myArgs["methodName"] as? String,
|
||||||
|
let message = myArgs["message"] as? String {
|
||||||
|
GetCocosPlayerUtils().postMessageToCocos(gameObject: gObj, cocosMethodName: method, cocosMessage: message)
|
||||||
|
result(nil)
|
||||||
|
} else {
|
||||||
|
result(FlutterError(code: "-1", message: "iOS could not extract " +
|
||||||
|
"flutter arguments in method: (postMessage)", details: nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,10 @@ public class FlutterCocosViewPlugin: NSObject, FlutterPlugin {
|
|||||||
let channel = FlutterMethodChannel(name: "flutter_cocos_view", binaryMessenger: registrar.messenger())
|
let channel = FlutterMethodChannel(name: "flutter_cocos_view", binaryMessenger: registrar.messenger())
|
||||||
let instance = FlutterCocosViewPlugin()
|
let instance = FlutterCocosViewPlugin()
|
||||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||||
|
|
||||||
|
let fuwFactory = FLTCocosWidgetFactory(registrar: registrar)
|
||||||
|
registrar.register(fuwFactory, withId: "plugin.gem.com/cocos_view", gestureRecognizersBlockingPolicy: FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
@ -17,3 +21,4 @@ public class FlutterCocosViewPlugin: NSObject, FlutterPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
ios/Classes/FlutterCocosWidgetPlugin.h
Normal file
13
ios/Classes/FlutterCocosWidgetPlugin.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// FlutterCocosWidgetPlugin.h
|
||||||
|
// FlutterCocosWidgetPlugin
|
||||||
|
//
|
||||||
|
// Created by Kris Pypen on 8/1/19.
|
||||||
|
// Updated by Rex Raphael on 8/27/2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
|
||||||
|
@interface FlutterCocosWidgetPlugin : NSObject<FlutterPlugin>
|
||||||
|
@end
|
||||||
|
|
22
ios/Classes/FlutterCocosWidgetPlugin.m
Normal file
22
ios/Classes/FlutterCocosWidgetPlugin.m
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#import FlutterCocosWidgetPlugin.h
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#if __has_include(<flutter_unity_widget/flutter_unity_widget-Swift.h>)
|
||||||
|
#import <flutter_unity_widget/flutter_unity_widget-Swift.h>
|
||||||
|
#else
|
||||||
|
// Support project import fallback if the generated compatibility header
|
||||||
|
// is not copied when this plugin is created as a library.
|
||||||
|
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
|
||||||
|
#import "flutter_unity_widget-Swift.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@implementation FlutterCocosWidgetPlugin {
|
||||||
|
NSObject<FlutterPluginRegistrar>* _registrar;
|
||||||
|
FlutterMethodChannel* _channel;
|
||||||
|
NSMutableDictionary* _unityControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||||
|
[SwiftFlutterCocosWidgetPlugin registerWithRegistrar:registrar];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -15,7 +15,7 @@ A new Flutter plugin project.
|
|||||||
s.source = { :path => '.' }
|
s.source = { :path => '.' }
|
||||||
s.source_files = 'Classes/**/*'
|
s.source_files = 'Classes/**/*'
|
||||||
s.dependency 'Flutter'
|
s.dependency 'Flutter'
|
||||||
s.platform = :ios, '12.0'
|
s.platform = :ios, '13.0'
|
||||||
|
|
||||||
# Flutter.framework does not contain a i386 slice.
|
# Flutter.framework does not contain a i386 slice.
|
||||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
// platforms in the `pubspec.yaml` at
|
// platforms in the `pubspec.yaml` at
|
||||||
// https://flutter.dev/to/pubspec-plugin-platforms.
|
// https://flutter.dev/to/pubspec-plugin-platforms.
|
||||||
|
|
||||||
import 'flutter_cocos_view_platform_interface.dart';
|
library flutter_Cocos_widget;
|
||||||
|
|
||||||
class FlutterCocosView {
|
export 'src/facade_controller.dart';
|
||||||
Future<String?> getPlatformVersion() {
|
export 'src/facade_widget.dart'
|
||||||
return FlutterCocosViewPlatform.instance.getPlatformVersion();
|
if (dart.library.io) 'src/io/cocos_widget.dart';
|
||||||
}
|
export 'src/helpers/events.dart';
|
||||||
}
|
export 'src/helpers/misc.dart';
|
||||||
|
export 'src/helpers/types.dart';
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'flutter_cocos_view_platform_interface.dart';
|
|
||||||
|
|
||||||
/// An implementation of [FlutterCocosViewPlatform] that uses method channels.
|
|
||||||
class MethodChannelFlutterCocosView extends FlutterCocosViewPlatform {
|
|
||||||
/// The method channel used to interact with the native platform.
|
|
||||||
@visibleForTesting
|
|
||||||
final methodChannel = const MethodChannel('flutter_cocos_view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getPlatformVersion() async {
|
|
||||||
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
||||||
|
|
||||||
import 'flutter_cocos_view_method_channel.dart';
|
|
||||||
|
|
||||||
abstract class FlutterCocosViewPlatform extends PlatformInterface {
|
|
||||||
/// Constructs a FlutterCocosViewPlatform.
|
|
||||||
FlutterCocosViewPlatform() : super(token: _token);
|
|
||||||
|
|
||||||
static final Object _token = Object();
|
|
||||||
|
|
||||||
static FlutterCocosViewPlatform _instance = MethodChannelFlutterCocosView();
|
|
||||||
|
|
||||||
/// The default instance of [FlutterCocosViewPlatform] to use.
|
|
||||||
///
|
|
||||||
/// Defaults to [MethodChannelFlutterCocosView].
|
|
||||||
static FlutterCocosViewPlatform get instance => _instance;
|
|
||||||
|
|
||||||
/// Platform-specific implementations should set this with their own
|
|
||||||
/// platform-specific class that extends [FlutterCocosViewPlatform] when
|
|
||||||
/// they register themselves.
|
|
||||||
static set instance(FlutterCocosViewPlatform instance) {
|
|
||||||
PlatformInterface.verifyToken(instance, _token);
|
|
||||||
_instance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> getPlatformVersion() {
|
|
||||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
103
lib/src/facade_controller.dart
Normal file
103
lib/src/facade_controller.dart
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
typedef void CocosCreatedCallback(CocosWidgetController controller);
|
||||||
|
|
||||||
|
abstract class CocosWidgetController {
|
||||||
|
static dynamic webRegistrar;
|
||||||
|
|
||||||
|
/// Method required for web initialization
|
||||||
|
static void registerWith(dynamic registrar) {
|
||||||
|
webRegistrar = registrar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize [CocosWidgetController] with [id]
|
||||||
|
/// Mainly for internal use when instantiating a [CocosWidgetController] passed
|
||||||
|
/// in [CocosWidget.onCocosCreated] callback.
|
||||||
|
static Future<CocosWidgetController> init(
|
||||||
|
int id, dynamic CocosWidgetState) async {
|
||||||
|
throw UnimplementedError('init() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks to see if Cocos player is ready to be used
|
||||||
|
/// Returns `true` if Cocos player is ready.
|
||||||
|
Future<bool?>? isReady() {
|
||||||
|
throw UnimplementedError('isReady() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current pause state of the Cocos player
|
||||||
|
/// Returns `true` if Cocos player is paused.
|
||||||
|
Future<bool?>? isPaused() {
|
||||||
|
throw UnimplementedError('isPaused() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current load state of the Cocos player
|
||||||
|
/// Returns `true` if Cocos player is loaded.
|
||||||
|
Future<bool?>? isLoaded() {
|
||||||
|
throw UnimplementedError('isLoaded() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to know if Cocos has been put in background mode (WIP) unstable
|
||||||
|
/// Returns `true` if Cocos player is in background.
|
||||||
|
Future<bool?>? inBackground() {
|
||||||
|
throw UnimplementedError('inBackground() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a Cocos player if it's not already created. Please only call this if Cocos is not ready,
|
||||||
|
/// or is in unloaded state. Use [isLoaded] to check.
|
||||||
|
/// Returns `true` if Cocos player was created succesfully.
|
||||||
|
Future<bool?>? create() {
|
||||||
|
throw UnimplementedError('create() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post message to Cocos from flutter. This method takes in a string [message].
|
||||||
|
/// The [gameObject] must match the name of an actual Cocos game object in a scene at runtime, and the [methodName],
|
||||||
|
/// must exist in a `MonoDevelop` `class` and also exposed as a method. [message] is an parameter taken by the method
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// postMessage("GameManager", "openScene", "ThirdScene")
|
||||||
|
/// ```
|
||||||
|
Future<void>? postMessage(String gameObject, methodName, message) {
|
||||||
|
throw UnimplementedError('postMessage() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post message to Cocos from flutter. This method takes in a Json or map structure as the [message].
|
||||||
|
/// The [gameObject] must match the name of an actual Cocos game object in a scene at runtime, and the [methodName],
|
||||||
|
/// must exist in a `MonoDevelop` `class` and also exposed as a method. [message] is an parameter taken by the method
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// postJsonMessage("GameManager", "openScene", {"buildIndex": 3, "name": "ThirdScene"})
|
||||||
|
/// ```
|
||||||
|
Future<void>? postJsonMessage(
|
||||||
|
String gameObject, String methodName, Map<String, dynamic> message) {
|
||||||
|
throw UnimplementedError('postJsonMessage() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pause the Cocos in-game player with this method
|
||||||
|
Future<void>? pause() {
|
||||||
|
throw UnimplementedError('pause() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resume the Cocos in-game player with this method idf it is in a paused state
|
||||||
|
Future<void>? resume() {
|
||||||
|
throw UnimplementedError('resume() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sometimes you want to open Cocos in it's own process and openInNativeProcess does just that.
|
||||||
|
/// It works for Android and iOS is WIP
|
||||||
|
Future<void>? openInNativeProcess() {
|
||||||
|
throw UnimplementedError('openInNativeProcess() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unloads Cocos player from th current process (Works on Android only for now)
|
||||||
|
/// iOS is WIP. For more information please read [Cocos Docs](https://docs.Cocos3d.com/2020.2/Documentation/Manual/CocosasaLibrary.html)
|
||||||
|
Future<void>? unload() {
|
||||||
|
throw UnimplementedError('unload() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quits Cocos player. Note that this kills the current flutter process, thus quiting the app
|
||||||
|
Future<void>? quit() {
|
||||||
|
throw UnimplementedError('quit() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
throw UnimplementedError('dispose() has not been implemented.');
|
||||||
|
}
|
||||||
|
}
|
89
lib/src/facade_widget.dart
Normal file
89
lib/src/facade_widget.dart
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter_cocos_view/src/facade_controller.dart';
|
||||||
|
|
||||||
|
import 'helpers/misc.dart';
|
||||||
|
|
||||||
|
class CocosWidget extends StatefulWidget {
|
||||||
|
CocosWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.onCocosCreated,
|
||||||
|
this.onCocosMessage,
|
||||||
|
this.fullscreen = false,
|
||||||
|
this.enablePlaceholder = false,
|
||||||
|
this.runImmediately = false,
|
||||||
|
this.unloadOnDispose = false,
|
||||||
|
this.printSetupLog = true,
|
||||||
|
this.onCocosUnloaded,
|
||||||
|
this.gestureRecognizers,
|
||||||
|
this.placeholder,
|
||||||
|
this.useAndroidViewSurface = false,
|
||||||
|
this.onCocosSceneLoaded,
|
||||||
|
this.uiLevel = 1,
|
||||||
|
this.borderRadius = BorderRadius.zero,
|
||||||
|
this.layoutDirection,
|
||||||
|
this.hideStatus = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
///Event fires when the Cocos player is created.
|
||||||
|
final CocosCreatedCallback onCocosCreated;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] gets a message from Cocos.
|
||||||
|
final CocosMessageCallback? onCocosMessage;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] gets a scene loaded from Cocos.
|
||||||
|
final CocosSceneChangeCallback? onCocosSceneLoaded;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] Cocos player gets unloaded.
|
||||||
|
final CocosUnloadCallback? onCocosUnloaded;
|
||||||
|
|
||||||
|
final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
|
||||||
|
|
||||||
|
/// Set to true to force Cocos to fullscreen
|
||||||
|
final bool fullscreen;
|
||||||
|
|
||||||
|
/// Set to true to force Cocos to fullscreen
|
||||||
|
final bool hideStatus;
|
||||||
|
|
||||||
|
/// Controls the layer in which Cocos widget is rendered in flutter (defaults to 1)
|
||||||
|
final int uiLevel;
|
||||||
|
|
||||||
|
/// This flag tells android to load Cocos as the flutter app starts (Android only)
|
||||||
|
final bool runImmediately;
|
||||||
|
|
||||||
|
/// This flag tells android to unload Cocos whenever widget is disposed
|
||||||
|
final bool unloadOnDispose;
|
||||||
|
|
||||||
|
/// This flag enables placeholder widget
|
||||||
|
final bool enablePlaceholder;
|
||||||
|
|
||||||
|
/// This flag enables placeholder widget
|
||||||
|
final bool printSetupLog;
|
||||||
|
|
||||||
|
/// This flag allows you use AndroidView instead of PlatformViewLink for android
|
||||||
|
final bool? useAndroidViewSurface;
|
||||||
|
|
||||||
|
/// This is just a helper to render a placeholder widget
|
||||||
|
final Widget? placeholder;
|
||||||
|
|
||||||
|
/// Border radius
|
||||||
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
|
/// The layout direction to use for the embedded view.
|
||||||
|
///
|
||||||
|
/// If this is null, the ambient [Directionality] is used instead. If there is
|
||||||
|
/// no ambient [Directionality], [TextDirection.ltr] is used.
|
||||||
|
final TextDirection? layoutDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CocosWidgetState createState() => _CocosWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CocosWidgetState extends State<CocosWidget> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(
|
||||||
|
'$defaultTargetPlatform is not yet supported by the Cocos player plugin');
|
||||||
|
}
|
||||||
|
}
|
36
lib/src/helpers/events.dart
Normal file
36
lib/src/helpers/events.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter_cocos_view/src/helpers/types.dart';
|
||||||
|
|
||||||
|
class CocosEvent<T> {
|
||||||
|
/// The ID of the Cocos this event is associated to.
|
||||||
|
final int cocosId;
|
||||||
|
|
||||||
|
/// The value wrapped by this event
|
||||||
|
final T value;
|
||||||
|
|
||||||
|
/// Build a Cocos Event, that relates a mapId with a given value.
|
||||||
|
///
|
||||||
|
/// The `cocosId` is the id of the map that triggered the event.
|
||||||
|
/// `value` may be `null` in events that don't transport any meaningful data.
|
||||||
|
CocosEvent(this.cocosId, this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CocosSceneLoadedEvent extends CocosEvent<SceneLoaded?> {
|
||||||
|
CocosSceneLoadedEvent(int cocosId, SceneLoaded? value)
|
||||||
|
: super(cocosId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CocosLoadedEvent extends CocosEvent<void> {
|
||||||
|
CocosLoadedEvent(int cocosId, void value) : super(cocosId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CocosUnLoadedEvent extends CocosEvent<void> {
|
||||||
|
CocosUnLoadedEvent(int cocosId, void value) : super(cocosId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CocosCreatedEvent extends CocosEvent<void> {
|
||||||
|
CocosCreatedEvent(int cocosId, void value) : super(cocosId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CocosMessageEvent extends CocosEvent<dynamic> {
|
||||||
|
CocosMessageEvent(int cocosId, dynamic value) : super(cocosId, value);
|
||||||
|
}
|
27
lib/src/helpers/misc.dart
Normal file
27
lib/src/helpers/misc.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter_cocos_view/src/helpers/types.dart';
|
||||||
|
|
||||||
|
/// Error thrown when an unknown cocos ID is provided to a method channel API.
|
||||||
|
class UnknownCocosIDError extends Error {
|
||||||
|
/// Creates an assertion error with the provided [cocosId] and optional
|
||||||
|
/// [message].
|
||||||
|
UnknownCocosIDError(this.cocosId, [this.message]);
|
||||||
|
|
||||||
|
/// The unknown ID.
|
||||||
|
final int cocosId;
|
||||||
|
|
||||||
|
/// Message describing the assertion error.
|
||||||
|
final Object? message;
|
||||||
|
|
||||||
|
String toString() {
|
||||||
|
if (message != null) {
|
||||||
|
return "Unknown cocos ID $cocosId: ${Error.safeToString(message)}";
|
||||||
|
}
|
||||||
|
return "Unknown cocos ID $cocosId";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void CocosMessageCallback(dynamic handler);
|
||||||
|
|
||||||
|
typedef void CocosSceneChangeCallback(SceneLoaded? message);
|
||||||
|
|
||||||
|
typedef void CocosUnloadCallback();
|
31
lib/src/helpers/types.dart
Normal file
31
lib/src/helpers/types.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
class SceneLoaded {
|
||||||
|
final String? name;
|
||||||
|
final int? buildIndex;
|
||||||
|
final bool? isLoaded;
|
||||||
|
final bool? isValid;
|
||||||
|
|
||||||
|
SceneLoaded({this.name, this.buildIndex, this.isLoaded, this.isValid});
|
||||||
|
|
||||||
|
/// Mainly for internal use when calling [CameraUpdate.newCameraPosition].
|
||||||
|
dynamic toMap() => <String, dynamic>{
|
||||||
|
'name': name,
|
||||||
|
'buildIndex': buildIndex,
|
||||||
|
'isLoaded': isLoaded,
|
||||||
|
'isValid': isValid,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Deserializes [SceneLoaded] from a map.
|
||||||
|
///
|
||||||
|
/// Mainly for internal use.
|
||||||
|
static SceneLoaded? fromMap(dynamic json) {
|
||||||
|
if (json == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return SceneLoaded(
|
||||||
|
name: json['name'],
|
||||||
|
buildIndex: json['buildIndex'],
|
||||||
|
isLoaded: json['isLoaded'],
|
||||||
|
isValid: json['isValid'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
201
lib/src/io/cocos_widget.dart
Normal file
201
lib/src/io/cocos_widget.dart
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../facade_controller.dart';
|
||||||
|
import '../helpers/misc.dart';
|
||||||
|
import 'device_method.dart';
|
||||||
|
import 'mobile_cocos_widget_controller.dart';
|
||||||
|
import 'cocos_widget_platform.dart';
|
||||||
|
|
||||||
|
int _nextCocosCreationId = 0;
|
||||||
|
|
||||||
|
/// Android specific settings for [CocosWidget].
|
||||||
|
class AndroidCocosWidgetFlutter {
|
||||||
|
/// Whether to render [CocosWidget] with a [AndroidViewSurface] to build the Flutter Cocos widget.
|
||||||
|
///
|
||||||
|
/// This implementation uses hybrid composition to render the Flutter Cocos
|
||||||
|
/// Widget on Android. This comes at the cost of some performance on Android
|
||||||
|
/// versions below 10. See
|
||||||
|
/// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more
|
||||||
|
/// information.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
static bool get useAndroidViewSurface {
|
||||||
|
final CocosWidgetPlatform platform = CocosWidgetPlatform.instance;
|
||||||
|
if (platform is MethodChannelCocosWidget) {
|
||||||
|
return platform.useAndroidViewSurface;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether to render [CocosWidget] with a [AndroidViewSurface] to build the Flutter Cocos widget.
|
||||||
|
///
|
||||||
|
/// This implementation uses hybrid composition to render the Cocos Widget
|
||||||
|
/// Widget on Android. This comes at the cost of some performance on Android
|
||||||
|
/// versions below 10. See
|
||||||
|
/// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more
|
||||||
|
/// information.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
static set useAndroidViewSurface(bool useAndroidViewSurface) {
|
||||||
|
final CocosWidgetPlatform platform = CocosWidgetPlatform.instance;
|
||||||
|
if (platform is MethodChannelCocosWidget) {
|
||||||
|
platform.useAndroidViewSurface = useAndroidViewSurface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef MobileCocosWidgetState = _CocosWidgetState;
|
||||||
|
|
||||||
|
class CocosWidget extends StatefulWidget {
|
||||||
|
CocosWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.onCocosCreated,
|
||||||
|
this.onCocosMessage,
|
||||||
|
this.fullscreen = false,
|
||||||
|
this.enablePlaceholder = false,
|
||||||
|
this.runImmediately = false,
|
||||||
|
this.unloadOnDispose = false,
|
||||||
|
this.printSetupLog = true,
|
||||||
|
this.onCocosUnloaded,
|
||||||
|
this.gestureRecognizers,
|
||||||
|
this.placeholder,
|
||||||
|
this.useAndroidViewSurface = false,
|
||||||
|
this.onCocosSceneLoaded,
|
||||||
|
this.uiLevel = 1,
|
||||||
|
this.borderRadius = BorderRadius.zero,
|
||||||
|
this.layoutDirection,
|
||||||
|
this.hideStatus = false,
|
||||||
|
this.webUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
///Event fires when the Cocos player is created.
|
||||||
|
final CocosCreatedCallback onCocosCreated;
|
||||||
|
|
||||||
|
/// WebGL url source.
|
||||||
|
final String? webUrl;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] gets a message from Cocos.
|
||||||
|
final CocosMessageCallback? onCocosMessage;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] gets a scene loaded from Cocos.
|
||||||
|
final CocosSceneChangeCallback? onCocosSceneLoaded;
|
||||||
|
|
||||||
|
///Event fires when the [CocosWidget] Cocos player gets unloaded.
|
||||||
|
final CocosUnloadCallback? onCocosUnloaded;
|
||||||
|
|
||||||
|
final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
|
||||||
|
|
||||||
|
/// Set to true to force Cocos to fullscreen
|
||||||
|
final bool fullscreen;
|
||||||
|
|
||||||
|
/// Set to true to force Cocos to fullscreen
|
||||||
|
final bool hideStatus;
|
||||||
|
|
||||||
|
/// Controls the layer in which Cocos widget is rendered in flutter (defaults to 1)
|
||||||
|
final int uiLevel;
|
||||||
|
|
||||||
|
/// This flag tells android to load Cocos as the flutter app starts (Android only)
|
||||||
|
final bool runImmediately;
|
||||||
|
|
||||||
|
/// This flag tells android to unload Cocos whenever widget is disposed
|
||||||
|
final bool unloadOnDispose;
|
||||||
|
|
||||||
|
/// This flag enables placeholder widget
|
||||||
|
final bool enablePlaceholder;
|
||||||
|
|
||||||
|
/// This flag enables placeholder widget
|
||||||
|
final bool printSetupLog;
|
||||||
|
|
||||||
|
/// This flag allows you use AndroidView instead of PlatformViewLink for android
|
||||||
|
final bool? useAndroidViewSurface;
|
||||||
|
|
||||||
|
/// This is just a helper to render a placeholder widget
|
||||||
|
final Widget? placeholder;
|
||||||
|
|
||||||
|
/// Border radius
|
||||||
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
|
/// The layout direction to use for the embedded view.
|
||||||
|
///
|
||||||
|
/// If this is null, the ambient [Directionality] is used instead. If there is
|
||||||
|
/// no ambient [Directionality], [TextDirection.ltr] is used.
|
||||||
|
final TextDirection? layoutDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CocosWidgetState createState() => _CocosWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CocosWidgetState extends State<CocosWidget> {
|
||||||
|
late int _CocosId;
|
||||||
|
|
||||||
|
CocosWidgetController? _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
if (!kIsWeb) {
|
||||||
|
_CocosId = _nextCocosCreationId++;
|
||||||
|
} else {
|
||||||
|
_CocosId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> dispose() async {
|
||||||
|
if (!kIsWeb) {
|
||||||
|
if (_nextCocosCreationId > 0) --_nextCocosCreationId;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
_controller?.dispose();
|
||||||
|
_controller = null;
|
||||||
|
} catch (e) {
|
||||||
|
// todo: implement
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Map<String, dynamic> CocosOptions = <String, dynamic>{
|
||||||
|
'fullscreen': widget.fullscreen,
|
||||||
|
'uiLevel': widget.uiLevel,
|
||||||
|
'hideStatus': widget.hideStatus,
|
||||||
|
'unloadOnDispose': widget.unloadOnDispose,
|
||||||
|
'runImmediately': widget.runImmediately,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (widget.enablePlaceholder) {
|
||||||
|
return widget.placeholder ??
|
||||||
|
Text('Placeholder mode enabled, no native code will be called');
|
||||||
|
}
|
||||||
|
|
||||||
|
return CocosWidgetPlatform.instance.buildViewWithTextDirection(
|
||||||
|
_CocosId,
|
||||||
|
_onPlatformViewCreated,
|
||||||
|
cocosOptions: CocosOptions,
|
||||||
|
textDirection: widget.layoutDirection ??
|
||||||
|
Directionality.maybeOf(context) ??
|
||||||
|
TextDirection.ltr,
|
||||||
|
gestureRecognizers: widget.gestureRecognizers,
|
||||||
|
useAndroidViewSurf: widget.useAndroidViewSurface,
|
||||||
|
cocosSrcUrl: widget.webUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onPlatformViewCreated(int id) async {
|
||||||
|
final controller = await MobileCocosWidgetController.init(id, this);
|
||||||
|
_controller = controller;
|
||||||
|
widget.onCocosCreated(controller);
|
||||||
|
|
||||||
|
if (widget.printSetupLog) {
|
||||||
|
log('*********************************************');
|
||||||
|
log('** flutter Cocos controller setup complete **');
|
||||||
|
log('*********************************************');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
lib/src/io/cocos_widget_platform.dart
Normal file
154
lib/src/io/cocos_widget_platform.dart
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
import '../helpers/events.dart';
|
||||||
|
import 'device_method.dart';
|
||||||
|
|
||||||
|
abstract class CocosWidgetPlatform extends PlatformInterface {
|
||||||
|
/// Constructs a CocosViewFlutterPlatform.
|
||||||
|
CocosWidgetPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static CocosWidgetPlatform _instance = MethodChannelCocosWidget();
|
||||||
|
|
||||||
|
/// The default instance of [CocosWidgetPlatform] to use.
|
||||||
|
///
|
||||||
|
/// Defaults to [MethodChannelCocosWidgetFlutter].
|
||||||
|
static CocosWidgetPlatform get instance => _instance;
|
||||||
|
|
||||||
|
/// Platform-specific plugins should set this with their own platform-specific
|
||||||
|
/// class that extends [CocosWidgetPlatform] when they register themselves.
|
||||||
|
static set instance(CocosWidgetPlatform instance) {
|
||||||
|
PlatformInterface.verifyToken(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// /// Initializes the platform interface with [id].
|
||||||
|
///
|
||||||
|
/// This method is called when the plugin is first initialized.
|
||||||
|
Future<void> init(int cocosId) {
|
||||||
|
throw UnimplementedError('init() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> isReady({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('init() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> isPaused({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('isPaused() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> isLoaded({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('isLoaded() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> inBackground({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('inBackground() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> createCocosPlayer({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('createCocosPlayer() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postMessage(
|
||||||
|
{required int cocosId,
|
||||||
|
required String gameObject,
|
||||||
|
required String methodName,
|
||||||
|
required String message}) {
|
||||||
|
throw UnimplementedError('postMessage() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postJsonMessage(
|
||||||
|
{required int cocosId,
|
||||||
|
required String gameObject,
|
||||||
|
required String methodName,
|
||||||
|
required Map message}) {
|
||||||
|
throw UnimplementedError('postJsonMessage() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pausePlayer({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('pausePlayer() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> resumePlayer({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('resumePlayer() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens cocos in it's own activity. Android only.
|
||||||
|
Future<void> openInNativeProcess({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('openInNativeProcess() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unloadPlayer({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('unloadPlayer() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> quitPlayer({required int cocosId}) async {
|
||||||
|
throw UnimplementedError('quitPlayer() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<CocosMessageEvent> onCocosMessage({required int cocosId}) {
|
||||||
|
throw UnimplementedError('onCocosMessage() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<CocosLoadedEvent> onCocosUnloaded({required int cocosId}) {
|
||||||
|
throw UnimplementedError('onCocosUnloaded() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<CocosCreatedEvent> onCocosCreated({required int cocosId}) {
|
||||||
|
throw UnimplementedError('onCocosUnloaded() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<CocosSceneLoadedEvent> onCocosSceneLoaded({required int cocosId}) {
|
||||||
|
throw UnimplementedError('onCocosSceneLoaded() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispose of whatever resources the `cocosId` is holding on to.
|
||||||
|
void dispose({required int cocosId}) {
|
||||||
|
throw UnimplementedError('dispose() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a widget displaying the cocos view
|
||||||
|
Widget buildView(
|
||||||
|
int creationId,
|
||||||
|
PlatformViewCreatedCallback onPlatformViewCreated, {
|
||||||
|
Map<String, dynamic> cocosOptions = const {},
|
||||||
|
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
|
||||||
|
bool? useAndroidViewSurf,
|
||||||
|
String? cocosSrcUrl,
|
||||||
|
}) {
|
||||||
|
throw UnimplementedError('buildView() has not been implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a widget displaying the cocos view.
|
||||||
|
///
|
||||||
|
/// This method is similar to [buildView], but contains a parameter for
|
||||||
|
/// platforms that require a text direction.
|
||||||
|
///
|
||||||
|
/// Default behavior passes all parameters except `textDirection` to
|
||||||
|
/// [buildView]. This is for backward compatibility with existing
|
||||||
|
/// implementations. Platforms that use the text direction should override
|
||||||
|
/// this as the primary implementation, and delegate to it from buildView.
|
||||||
|
Widget buildViewWithTextDirection(
|
||||||
|
int creationId,
|
||||||
|
PlatformViewCreatedCallback onPlatformViewCreated, {
|
||||||
|
required TextDirection textDirection,
|
||||||
|
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
|
||||||
|
Map<String, dynamic> cocosOptions = const <String, dynamic>{},
|
||||||
|
bool? useAndroidViewSurf,
|
||||||
|
String? cocosSrcUrl,
|
||||||
|
}) {
|
||||||
|
return buildView(
|
||||||
|
creationId,
|
||||||
|
onPlatformViewCreated,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
cocosOptions: cocosOptions,
|
||||||
|
useAndroidViewSurf: useAndroidViewSurf,
|
||||||
|
cocosSrcUrl: cocosSrcUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
299
lib/src/io/device_method.dart
Normal file
299
lib/src/io/device_method.dart
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:stream_transform/stream_transform.dart';
|
||||||
|
|
||||||
|
import '../helpers/events.dart';
|
||||||
|
import '../helpers/misc.dart';
|
||||||
|
import '../helpers/types.dart';
|
||||||
|
import 'cocos_widget_platform.dart';
|
||||||
|
import 'windows_cocos_widget_view.dart';
|
||||||
|
|
||||||
|
class MethodChannelCocosWidget extends CocosWidgetPlatform {
|
||||||
|
// Every method call passes the int cocosId
|
||||||
|
late final Map<int, MethodChannel> _channels = {};
|
||||||
|
|
||||||
|
/// Set [CocosWidgetFlutterPlatform] to use [AndroidViewSurface] to build the Google Maps widget.
|
||||||
|
///
|
||||||
|
/// This implementation uses hybrid composition to render the Cocos Widget
|
||||||
|
/// Widget on Android. This comes at the cost of some performance on Android
|
||||||
|
/// versions below 10. See
|
||||||
|
/// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more
|
||||||
|
/// information.
|
||||||
|
/// Defaults to false.
|
||||||
|
bool useAndroidViewSurface = true;
|
||||||
|
|
||||||
|
/// Accesses the MethodChannel associated to the passed cocosId.
|
||||||
|
MethodChannel channel(int cocosId) {
|
||||||
|
MethodChannel? channel = _channels[cocosId];
|
||||||
|
if (channel == null) {
|
||||||
|
throw UnknownCocosIDError(cocosId);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodChannel ensureChannelInitialized(int cocosId) {
|
||||||
|
MethodChannel? channel = _channels[cocosId];
|
||||||
|
if (channel == null) {
|
||||||
|
channel = MethodChannel('plugin.xraph.com/cocos_view_$cocosId');
|
||||||
|
|
||||||
|
channel.setMethodCallHandler(
|
||||||
|
(MethodCall call) => _handleMethodCall(call, cocosId));
|
||||||
|
_channels[cocosId] = channel;
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the platform interface with [id].
|
||||||
|
///
|
||||||
|
/// This method is called when the plugin is first initialized.
|
||||||
|
@override
|
||||||
|
Future<void> init(int cocosId) {
|
||||||
|
MethodChannel channel = ensureChannelInitialized(cocosId);
|
||||||
|
return channel.invokeMethod<void>('cocos#waitForCocos');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispose of the native resources.
|
||||||
|
@override
|
||||||
|
Future<void> dispose({int? cocosId}) async {
|
||||||
|
try {
|
||||||
|
if (cocosId != null) await channel(cocosId).invokeMethod('cocos#dispose');
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The controller we need to broadcast the different events coming
|
||||||
|
// from handleMethodCall.
|
||||||
|
//
|
||||||
|
// It is a `broadcast` because multiple controllers will connect to
|
||||||
|
// different stream views of this Controller.
|
||||||
|
final StreamController<CocosEvent> _cocosStreamController =
|
||||||
|
StreamController<CocosEvent>.broadcast();
|
||||||
|
|
||||||
|
// Returns a filtered view of the events in the _controller, by cocosId.
|
||||||
|
Stream<CocosEvent> _events(int cocosId) =>
|
||||||
|
_cocosStreamController.stream.where((event) => event.cocosId == cocosId);
|
||||||
|
|
||||||
|
Future<dynamic> _handleMethodCall(MethodCall call, int cocosId) async {
|
||||||
|
switch (call.method) {
|
||||||
|
case "events#onCocosMessage":
|
||||||
|
_cocosStreamController.add(CocosMessageEvent(cocosId, call.arguments));
|
||||||
|
break;
|
||||||
|
case "events#onCocosUnloaded":
|
||||||
|
_cocosStreamController.add(CocosLoadedEvent(cocosId, call.arguments));
|
||||||
|
break;
|
||||||
|
case "events#onCocosSceneLoaded":
|
||||||
|
_cocosStreamController.add(CocosSceneLoadedEvent(
|
||||||
|
cocosId, SceneLoaded.fromMap(call.arguments)));
|
||||||
|
break;
|
||||||
|
case "events#onCocosCreated":
|
||||||
|
_cocosStreamController.add(CocosCreatedEvent(cocosId, call.arguments));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> isPaused({required int cocosId}) async {
|
||||||
|
return await channel(cocosId).invokeMethod('cocos#isPaused');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> isReady({required int cocosId}) async {
|
||||||
|
return await channel(cocosId).invokeMethod('cocos#isReady');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> isLoaded({required int cocosId}) async {
|
||||||
|
return await channel(cocosId).invokeMethod('cocos#isLoaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> inBackground({required int cocosId}) async {
|
||||||
|
return await channel(cocosId).invokeMethod('cocos#inBackground');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool?> createCocosPlayer({required int cocosId}) async {
|
||||||
|
return await channel(cocosId).invokeMethod('cocos#createPlayer');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<CocosMessageEvent> onCocosMessage({required int cocosId}) {
|
||||||
|
return _events(cocosId).whereType<CocosMessageEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<CocosLoadedEvent> onCocosUnloaded({required int cocosId}) {
|
||||||
|
return _events(cocosId).whereType<CocosLoadedEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<CocosCreatedEvent> onCocosCreated({required int cocosId}) {
|
||||||
|
return _events(cocosId).whereType<CocosCreatedEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<CocosSceneLoadedEvent> onCocosSceneLoaded({required int cocosId}) {
|
||||||
|
return _events(cocosId).whereType<CocosSceneLoadedEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildViewWithTextDirection(
|
||||||
|
int creationId,
|
||||||
|
PlatformViewCreatedCallback onPlatformViewCreated, {
|
||||||
|
required TextDirection textDirection,
|
||||||
|
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
|
||||||
|
Map<String, dynamic> cocosOptions = const <String, dynamic>{},
|
||||||
|
bool? useAndroidViewSurf,
|
||||||
|
bool? height,
|
||||||
|
bool? width,
|
||||||
|
bool? cocosWebSource,
|
||||||
|
String? cocosSrcUrl,
|
||||||
|
}) {
|
||||||
|
final String _viewType = 'plugin.xraph.com/cocos_view';
|
||||||
|
|
||||||
|
if (useAndroidViewSurf != null) useAndroidViewSurface = useAndroidViewSurf;
|
||||||
|
|
||||||
|
final Map<String, dynamic> creationParams = cocosOptions;
|
||||||
|
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.windows) {
|
||||||
|
return WindowsCocosWidgetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
|
if (!useAndroidViewSurface) {
|
||||||
|
return AndroidView(
|
||||||
|
viewType: _viewType,
|
||||||
|
onPlatformViewCreated: onPlatformViewCreated,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
creationParams: creationParams,
|
||||||
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
|
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlatformViewLink(
|
||||||
|
viewType: _viewType,
|
||||||
|
surfaceFactory: (
|
||||||
|
BuildContext context,
|
||||||
|
PlatformViewController controller,
|
||||||
|
) {
|
||||||
|
return AndroidViewSurface(
|
||||||
|
controller: controller as AndroidViewController,
|
||||||
|
gestureRecognizers: gestureRecognizers ??
|
||||||
|
const <Factory<OneSequenceGestureRecognizer>>{},
|
||||||
|
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onCreatePlatformView: (PlatformViewCreationParams params) {
|
||||||
|
final controller = PlatformViewsService.initExpensiveAndroidView(
|
||||||
|
id: params.id,
|
||||||
|
viewType: _viewType,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
|
creationParams: creationParams,
|
||||||
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
|
onFocus: () => params.onFocusChanged(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
controller
|
||||||
|
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
|
||||||
|
..addOnPlatformViewCreatedListener(onPlatformViewCreated)
|
||||||
|
..create();
|
||||||
|
return controller;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||||
|
return UiKitView(
|
||||||
|
viewType: _viewType,
|
||||||
|
onPlatformViewCreated: onPlatformViewCreated,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
creationParams: creationParams,
|
||||||
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Text(
|
||||||
|
'$defaultTargetPlatform is not yet supported by the cocos player plugin');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildView(
|
||||||
|
int creationId,
|
||||||
|
PlatformViewCreatedCallback onPlatformViewCreated, {
|
||||||
|
Map<String, dynamic> cocosOptions = const {},
|
||||||
|
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
|
||||||
|
bool? useAndroidViewSurf,
|
||||||
|
String? cocosSrcUrl,
|
||||||
|
}) {
|
||||||
|
return buildViewWithTextDirection(
|
||||||
|
creationId,
|
||||||
|
onPlatformViewCreated,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
cocosOptions: cocosOptions,
|
||||||
|
useAndroidViewSurf: useAndroidViewSurf,
|
||||||
|
cocosSrcUrl: cocosSrcUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> postMessage({
|
||||||
|
required int cocosId,
|
||||||
|
required String gameObject,
|
||||||
|
required String methodName,
|
||||||
|
required String message,
|
||||||
|
}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#postMessage', <String, dynamic>{
|
||||||
|
'gameObject': gameObject,
|
||||||
|
'methodName': methodName,
|
||||||
|
'message': message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> postJsonMessage({
|
||||||
|
required int cocosId,
|
||||||
|
required String gameObject,
|
||||||
|
required String methodName,
|
||||||
|
required Map message,
|
||||||
|
}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#postMessage', <String, dynamic>{
|
||||||
|
'gameObject': gameObject,
|
||||||
|
'methodName': methodName,
|
||||||
|
'message': json.encode(message),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> pausePlayer({required int cocosId}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#pausePlayer');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> resumePlayer({required int cocosId}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#resumePlayer');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> openInNativeProcess({required int cocosId}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#openInNativeProcess');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> unloadPlayer({required int cocosId}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#unloadPlayer');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> quitPlayer({required int cocosId}) async {
|
||||||
|
await channel(cocosId).invokeMethod('cocos#quitPlayer');
|
||||||
|
}
|
||||||
|
}
|
4
lib/src/io/io.dart
Normal file
4
lib/src/io/io.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export '../facade_controller.dart';
|
||||||
|
export '../helpers/events.dart';
|
||||||
|
export '../helpers/misc.dart';
|
||||||
|
export '../helpers/types.dart';
|
213
lib/src/io/mobile_cocos_widget_controller.dart
Normal file
213
lib/src/io/mobile_cocos_widget_controller.dart
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import '../facade_controller.dart';
|
||||||
|
import '../helpers/events.dart';
|
||||||
|
import 'device_method.dart';
|
||||||
|
import 'cocos_widget.dart';
|
||||||
|
import 'cocos_widget_platform.dart';
|
||||||
|
|
||||||
|
class MobileCocosWidgetController extends CocosWidgetController {
|
||||||
|
final MobileCocosWidgetState _cocosWidgetState;
|
||||||
|
|
||||||
|
/// The cocosId for this controller
|
||||||
|
final int cocosId;
|
||||||
|
|
||||||
|
/// used for cancel the subscription
|
||||||
|
StreamSubscription? _onCocosMessageSub,
|
||||||
|
_onCocosSceneLoadedSub,
|
||||||
|
_onCocosUnloadedSub;
|
||||||
|
|
||||||
|
MobileCocosWidgetController._(this._cocosWidgetState,
|
||||||
|
{required this.cocosId}) {
|
||||||
|
_connectStreams(cocosId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize [CocosWidgetController] with [id]
|
||||||
|
/// Mainly for internal use when instantiating a [CocosWidgetController] passed
|
||||||
|
/// in [CocosWidget.onCocosCreated] callback.
|
||||||
|
static Future<MobileCocosWidgetController> init(
|
||||||
|
int id, MobileCocosWidgetState cocosWidgetState) async {
|
||||||
|
await CocosWidgetPlatform.instance.init(id);
|
||||||
|
return MobileCocosWidgetController._(
|
||||||
|
cocosWidgetState,
|
||||||
|
cocosId: id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
MethodChannel? get channel {
|
||||||
|
if (CocosWidgetPlatform.instance is MethodChannelCocosWidget) {
|
||||||
|
return (CocosWidgetPlatform.instance as MethodChannelCocosWidget)
|
||||||
|
.channel(cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _connectStreams(int cocosId) {
|
||||||
|
if (_cocosWidgetState.widget.onCocosMessage != null) {
|
||||||
|
_onCocosMessageSub = CocosWidgetPlatform.instance
|
||||||
|
.onCocosMessage(cocosId: cocosId)
|
||||||
|
.listen((CocosMessageEvent e) =>
|
||||||
|
_cocosWidgetState.widget.onCocosMessage!(e.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cocosWidgetState.widget.onCocosSceneLoaded != null) {
|
||||||
|
_onCocosSceneLoadedSub = CocosWidgetPlatform.instance
|
||||||
|
.onCocosSceneLoaded(cocosId: cocosId)
|
||||||
|
.listen((CocosSceneLoadedEvent e) =>
|
||||||
|
_cocosWidgetState.widget.onCocosSceneLoaded!(e.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cocosWidgetState.widget.onCocosUnloaded != null) {
|
||||||
|
_onCocosUnloadedSub = CocosWidgetPlatform.instance
|
||||||
|
.onCocosUnloaded(cocosId: cocosId)
|
||||||
|
.listen((_) => _cocosWidgetState.widget.onCocosUnloaded!());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks to see if cocos player is ready to be used
|
||||||
|
/// Returns `true` if cocos player is ready.
|
||||||
|
Future<bool?>? isReady() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.isReady(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current pause state of the cocos player
|
||||||
|
/// Returns `true` if cocos player is paused.
|
||||||
|
Future<bool?>? isPaused() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.isPaused(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current load state of the cocos player
|
||||||
|
/// Returns `true` if cocos player is loaded.
|
||||||
|
Future<bool?>? isLoaded() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.isLoaded(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to know if Cocos has been put in background mode (WIP) unstable
|
||||||
|
/// Returns `true` if cocos player is in background.
|
||||||
|
Future<bool?>? inBackground() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.inBackground(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a cocos player if it's not already created. Please only call this if cocos is not ready,
|
||||||
|
/// or is in unloaded state. Use [isLoaded] to check.
|
||||||
|
/// Returns `true` if cocos player was created succesfully.
|
||||||
|
Future<bool?>? create() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.createCocosPlayer(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post message to cocos from flutter. This method takes in a string [message].
|
||||||
|
/// The [gameObject] must match the name of an actual cocos game object in a scene at runtime, and the [methodName],
|
||||||
|
/// must exist in a `MonoDevelop` `class` and also exposed as a method. [message] is an parameter taken by the method
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// postMessage("GameManager", "openScene", "ThirdScene")
|
||||||
|
/// ```
|
||||||
|
Future<void>? postMessage(String gameObject, methodName, message) {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.postMessage(
|
||||||
|
cocosId: cocosId,
|
||||||
|
gameObject: gameObject,
|
||||||
|
methodName: methodName,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Post message to cocos from flutter. This method takes in a Json or map structure as the [message].
|
||||||
|
/// The [gameObject] must match the name of an actual cocos game object in a scene at runtime, and the [methodName],
|
||||||
|
/// must exist in a `MonoDevelop` `class` and also exposed as a method. [message] is an parameter taken by the method
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// postJsonMessage("GameManager", "openScene", {"buildIndex": 3, "name": "ThirdScene"})
|
||||||
|
/// ```
|
||||||
|
Future<void>? postJsonMessage(
|
||||||
|
String gameObject, String methodName, Map<String, dynamic> message) {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.postJsonMessage(
|
||||||
|
cocosId: cocosId,
|
||||||
|
gameObject: gameObject,
|
||||||
|
methodName: methodName,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pause the cocos in-game player with this method
|
||||||
|
Future<void>? pause() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.pausePlayer(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resume the cocos in-game player with this method idf it is in a paused state
|
||||||
|
Future<void>? resume() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.resumePlayer(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sometimes you want to open cocos in it's own process and openInNativeProcess does just that.
|
||||||
|
/// It works for Android and iOS is WIP
|
||||||
|
Future<void>? openInNativeProcess() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.openInNativeProcess(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unloads cocos player from th current process (Works on Android only for now)
|
||||||
|
/// iOS is WIP. For more information please read [Cocos Docs](https://docs.cocos3d.com/2020.2/Documentation/Manual/CocosasaLibrary.html)
|
||||||
|
Future<void>? unload() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.unloadPlayer(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quits cocos player. Note that this kills the current flutter process, thus quiting the app
|
||||||
|
Future<void>? quit() {
|
||||||
|
if (!_cocosWidgetState.widget.enablePlaceholder) {
|
||||||
|
return CocosWidgetPlatform.instance.quitPlayer(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// cancel the subscriptions when dispose called
|
||||||
|
void _cancelSubscriptions() {
|
||||||
|
_onCocosMessageSub?.cancel();
|
||||||
|
_onCocosSceneLoadedSub?.cancel();
|
||||||
|
_onCocosUnloadedSub?.cancel();
|
||||||
|
|
||||||
|
_onCocosMessageSub = null;
|
||||||
|
_onCocosSceneLoadedSub = null;
|
||||||
|
_onCocosUnloadedSub = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_cancelSubscriptions();
|
||||||
|
CocosWidgetPlatform.instance.dispose(cocosId: cocosId);
|
||||||
|
}
|
||||||
|
}
|
20
lib/src/io/windows_cocos_widget_view.dart
Normal file
20
lib/src/io/windows_cocos_widget_view.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class WindowsCocosWidgetView extends StatefulWidget {
|
||||||
|
const WindowsCocosWidgetView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WindowsCocosWidgetView> createState() => _WindowsCocosWidgetViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WindowsCocosWidgetViewState extends State<WindowsCocosWidgetView> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// TODO: Rex Update windows view
|
||||||
|
return const MouseRegion(
|
||||||
|
child: Texture(
|
||||||
|
textureId: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,14 @@ version: 0.0.1
|
|||||||
homepage:
|
homepage:
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.4.4
|
||||||
flutter: '>=3.3.0'
|
flutter: '>=3.3.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
plugin_platform_interface: ^2.0.2
|
plugin_platform_interface: ^2.0.2
|
||||||
|
stream_transform: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_cocos_view/flutter_cocos_view_method_channel.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
MethodChannelFlutterCocosView platform = MethodChannelFlutterCocosView();
|
|
||||||
const MethodChannel channel = MethodChannel('flutter_cocos_view');
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
|
||||||
channel,
|
|
||||||
(MethodCall methodCall) async {
|
|
||||||
return '42';
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() {
|
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getPlatformVersion', () async {
|
|
||||||
expect(await platform.getPlatformVersion(), '42');
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:flutter_cocos_view/flutter_cocos_view.dart';
|
|
||||||
import 'package:flutter_cocos_view/flutter_cocos_view_platform_interface.dart';
|
|
||||||
import 'package:flutter_cocos_view/flutter_cocos_view_method_channel.dart';
|
|
||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
||||||
|
|
||||||
class MockFlutterCocosViewPlatform
|
|
||||||
with MockPlatformInterfaceMixin
|
|
||||||
implements FlutterCocosViewPlatform {
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getPlatformVersion() => Future.value('42');
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final FlutterCocosViewPlatform initialPlatform = FlutterCocosViewPlatform.instance;
|
|
||||||
|
|
||||||
test('$MethodChannelFlutterCocosView is the default instance', () {
|
|
||||||
expect(initialPlatform, isInstanceOf<MethodChannelFlutterCocosView>());
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getPlatformVersion', () async {
|
|
||||||
FlutterCocosView flutterCocosViewPlugin = FlutterCocosView();
|
|
||||||
MockFlutterCocosViewPlatform fakePlatform = MockFlutterCocosViewPlatform();
|
|
||||||
FlutterCocosViewPlatform.instance = fakePlatform;
|
|
||||||
|
|
||||||
expect(await flutterCocosViewPlugin.getPlatformVersion(), '42');
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user