繼上一篇文章提到了用 Channel 實作 Flutter 和 Android/iOS 之間的通訊。本文將會示範如何用 Channel 來監聽音量按鈕的事件,因為我手邊只有 Android 裝置,所以會用 Kotlin 來示範。
EH Redux 有一個功能就是能夠使用音量鍵來控制圖片翻頁,這項功能因為目前 Flutter 還沒有官方支援,所以必須要在 Android/iOS 這邊自己寫程式去補足。
MethodChannel 用於單次的非同步執行,這次我會用在切換音量控制是否開啟,因為 Flutter 在 Android 上預設只會有一個 FlutterActivity,無論在哪個畫面背後實際上都是同一個 activity,而音量控制只會用在圖片瀏覽的畫面上,所以必須動態切換音量控制,否則在其他畫面上也會受到影響。
首先是 Android 的部分,在 MainActivity
裡實作 configureFlutterEngine
方法,然後在裡面建立一個 MethodChannel
,用來監聽從 Flutter 傳來的事件。
// 用這個變數來控制要不要攔截 keydown 事件private var interceptKeyDownEnabled = falseoverride fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // Method channel 的名字可以隨便取,只要確保在 Android/iOS 和 Flutter 兩邊用的名字一致就好了 MethodChannel(flutterEngine.dartExecutor, "com.example/method").setMethodCallHandler { call, result -> when (call.method) { "interceptKeyDown" -> { interceptKeyDownEnabled = true // 如果成功的話就用 result.success 回傳結果 result.success(true) // 失敗的話則是用 result.error 回傳錯誤 // result.error("ERROR_CODE", "error message", null) } "uninterceptKeyDown" -> { interceptKeyDownEnabled = false result.success(true) } else -> { // 其他沒有 handle 到的 method 就回傳 result.notImplemented result.notImplemented() } } }}override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (interceptKeyDownEnabled) { // 攔截 keydown 事件 return true } return super.onKeyDown(keyCode, event)}
接下來是 Flutter 的部分,這部分比較簡單,用 MethodChannel
的 invokeMethod
就能執行上面在 Android 定義的程式碼。
final methodChannel = MethodChannel('com.example/method');// 開始攔截 keydown 事件await methodChannel.invokeMethod('interceptKeyDown');// 停止攔截 keydown 事件await methodChannel.invokeMethod('uninterceptKeyDown');
到此為止就能夠從 Flutter 切換音量控制了。
EventChannel 讓 Flutter 能夠監聽從 Android/iOS 傳來的事件,這次會用在監聽 keydown 事件。
首先是 Android 的部分,在原本的 configureFlutterEngine
額外新增了一個 EventChannel
,並用 Rx subject 來傳遞 keydown 事件。
private var interceptKeyDownEnabled = false// 用 Rx 來傳遞事件,也可以改用其他類似的 libraryprivate val keyDownSubject = PublishSubject.create<String>()override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor, "com.example/method").setMethodCallHandler { call, result -> // ... } // Event channel 的名字可以隨便取,只要確保在 Android/iOS 和 Flutter 兩邊用的名字一致就好了 EventChannel(flutterEngine.dartExecutor, "com.example/event").setStreamHandler(object : StreamHandler { var dispose: Disposable? = null override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { // 開始訂閱事件 dispose = keyDownSubject.subscribeBy ( onNext = { events?.success(it) }, onError = { events?.error("KEY_DOWN_EVENT", it.message, it) }, onComplete = { events?.endOfStream() } ) } override fun onCancel(arguments: Any?) { // 停止訂閱事件 dispose?.dispose() dispose = null } })}override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (interceptKeyDownEnabled) { // 在按下音量 +/- 鍵的時候,送事件到 Rx subject 並攔截 keydown 事件 when (keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { keyDownSubject.onNext("volumeDown") return true } KeyEvent.KEYCODE_VOLUME_UP -> { keyDownSubject.onNext("volumeUp") return true } } } return super.onKeyDown(keyCode, event)}
接下來是 Flutter 的部分,用 EventChannel
的 receiveBroadcastStream
就能接收事件。
final eventChannel = EventChannel('com.example/event');// 訂閱事件final subscription = eventChannel.receiveBroadcastStream().listen((event) { final code = event as String; // ...});// 取消訂閱subscription.cancel();
這樣就能從 Flutter 監聽音量鍵的事件了。實際上的範例可以參考 EH Redux 的 MainActivity.kt 和 key_event.dart;更複雜一點的可以參考 hardware_buttons,它同時實作了 Android 和 iOS 的部分。
上上週的時候把 P5S 玩完一輪了,這真的是一款非常優秀的遊戲,與其說是無雙,不如說更像動作 RPG,需要花一點時間適應;而劇情上也很不錯,補完了一些原本在本傳裡戲份比較少的角色的劇情,像是佑介和春,感覺角色更加生動了。
那麼究竟是為什麼明明遊戲都玩完了,卻還是沒有繼續開發 app 呢,主要是因為最近接觸到赤井はあと拍的一堆狂氣廢片後,讓我開始踏入 Hololive 的坑,又開始浪費時間看 Vtuber 了😜。我預計從這周末開始應該就會重啟開發,應該吧。