플랫폼 채널: Swift와 Kotlin 네이티브 코드 통합
Flutter는 앱 기능의 90%에 적합하지만 존재합니다. 플랫폼의 기본 API에 직접 액세스해야 하는 경우: 생체 인식 센서, Bluetooth LE, Apple Pay, Google Pay, 파일 시스템 액세스 기본, Flutter 플러그인으로 사용할 수 없는 타사 SDK와의 통합. 이러한 경우에는 플랫폼 채널.
플랫폼 채널은 Dart 간의 양방향 통신 브리지를 만듭니다. 및 네이티브 코드(iOS에서는 Swift, Android에서는 Kotlin). 당신은 자바를 작성하고 있지 않습니다 o 레거시 Objective-C: 코루틴 및 async/await가 포함된 최신 Kotlin 및 Swift 각각. 이 가이드에서는 기기의 근접 센서에 연결 — Flutter Core에서는 사용할 수 없습니다.
무엇을 배울 것인가
- MethodChannel: Dart → 응답(요청/응답)이 포함된 기본 호출
- EventChannel: 네이티브 → Dart의 지속적인 데이터 스트림
- BasicMessageChannel: 구조화되지 않은 양방향 통신
- 오류 처리: 양방향의 PlatformException
- 스레딩: 메인 스레드, 백그라운드 스레드, FlutterEngine
- 플러그인 패키지: 재사용 가능한 플러그인의 구조
- Pigeon: 플랫폼 채널을 위한 유형 안전 코드 생성
MethodChannel: 동기식 요청/응답
Il 메소드채널 가장 일반적인 채널 유형: Dart 코드 네이티브 메소드를 호출하고 응답을 기다립니다. 원샷 작업에 이상적 장치에 대한 정보를 얻는 방법, 기본 화면을 여는 방법, 생체 인식 작업을 수행합니다.
// lib/proximity_sensor.dart: interfaccia Dart
import 'package:flutter/services.dart';
class ProximitySensor {
static const _channel = MethodChannel('com.example.proximity_sensor');
// Verifica se il sensore e disponibile
static Future<bool> isAvailable() async {
try {
final bool result = await _channel.invokeMethod('isAvailable');
return result;
} on PlatformException catch (e) {
// PlatformException: errore dal codice nativo
debugPrint('ProximitySensor.isAvailable error: ${e.code} - ${e.message}');
return false;
} on MissingPluginException {
// Plugin non registrato (es. desktop non supporta il sensore)
return false;
}
}
// Ottieni la distanza attuale (in cm, 0 = vicino, null = non disponibile)
static Future<double?> getDistance() async {
try {
final double? distance = await _channel.invokeMethod<double>('getDistance');
return distance;
} on PlatformException catch (e) {
throw ProximitySensorException(e.code, e.message ?? 'Unknown error');
}
}
}
class ProximitySensorException implements Exception {
final String code;
final String message;
const ProximitySensorException(this.code, this.message);
@override
String toString() => 'ProximitySensorException($code): $message';
}
// android/src/main/kotlin/.../ProximitySensorPlugin.kt
// Implementazione Android con Kotlin
package com.example.proximity_sensor
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorManager
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class ProximitySensorPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
private lateinit var channel: MethodChannel
private lateinit var context: Context
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
context = binding.applicationContext
channel = MethodChannel(
binding.binaryMessenger,
"com.example.proximity_sensor"
)
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"isAvailable" -> {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
result.success(sensor != null)
}
"getDistance" -> {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
if (sensor == null) {
// Errore: ritorna PlatformException a Dart
result.error(
"SENSOR_NOT_FOUND",
"Proximity sensor not available on this device",
null
)
return
}
// Leggi il valore attuale (semplificato: usa EventChannel per stream)
result.success(sensor.maximumRange.toDouble())
}
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
// ios/Classes/ProximitySensorPlugin.swift
// Implementazione iOS con Swift
import Flutter
import UIKit
public class ProximitySensorPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "com.example.proximity_sensor",
binaryMessenger: registrar.messenger()
)
let instance = ProximitySensorPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "isAvailable":
// iOS ha sempre il sensore di prossimita nei modelli standard
result(UIDevice.current.isProximityMonitoringEnabled ||
ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] == nil)
case "getDistance":
UIDevice.current.isProximityMonitoringEnabled = true
let isNear = UIDevice.current.proximityState
// Conversione in float: 0.0 = lontano, 1.0 = vicino
let distance: Double = isNear ? 0.0 : 10.0
result(distance)
default:
result(FlutterMethodNotImplemented)
}
}
}
EventChannel: 지속적인 데이터 스트림
L'이벤트채널 스트리밍 데이터용으로 설계됨: 센서 지속적으로 업데이트되며 지리적 위치 이벤트, 하드웨어 알림이 제공됩니다. 네이티브는 위젯이 들을 수 있는 Dart 스트림으로 이벤트를 내보냅니다. StreamBuilder.
// Dart: EventChannel per stream del sensore di prossimita
class ProximitySensorStream {
static const _eventChannel = EventChannel('com.example.proximity_sensor/stream');
static Stream<double> get proximityStream {
return _eventChannel
.receiveBroadcastStream()
.map((event) => (event as double));
}
}
// Widget: StreamBuilder per visualizzare i dati in tempo reale
class ProximitySensorWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<double>(
stream: ProximitySensorStream.proximityStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Errore: ${snapshot.error}');
}
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
final distance = snapshot.data!;
return Column(
children: [
Text('Distanza: ${distance.toStringAsFixed(1)} cm'),
Icon(
distance < 5 ? Icons.phone_in_talk : Icons.phone_android,
size: 48,
color: distance < 5 ? Colors.red : Colors.green,
),
],
);
},
);
}
}
// Kotlin: EventChannel sul lato Android
class ProximitySensorStreamHandler(
private val context: Context
) : EventChannel.StreamHandler {
private var sensorManager: SensorManager? = null
private var sensorListener: SensorEventListener? = null
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = sensorManager?.getDefaultSensor(Sensor.TYPE_PROXIMITY)
if (sensor == null) {
events.error("SENSOR_NOT_FOUND", "No proximity sensor", null)
return
}
sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
// Invia ogni aggiornamento al Dart stream
events.success(event.values[0].toDouble())
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
sensorManager?.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onCancel(arguments: Any?) {
// Pulisci le risorse quando lo stream viene cancellato
sensorManager?.unregisterListener(sensorListener)
sensorManager = null
sensorListener = null
}
}
Pigeon: 코드 생성 유형 안전
비둘기 코드를 생성하는 공식 Flutter 도구 공유 Dart 정의의 플랫폼 채널 상용구입니다. 수동 일련화 및 보증을 관리할 필요가 없습니다. Dart와 네이티브 코드가 동기화되어 있습니다.
// pigeons/proximity.dart: definizione dell'interfaccia (source of truth)
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/proximity_api.g.dart',
kotlinOut: 'android/src/main/kotlin/com/example/ProximityApi.g.kt',
swiftOut: 'ios/Classes/ProximityApi.g.swift',
))
// Messaggi (data classes)
class ProximityInfo {
final double distance;
final bool isNear;
const ProximityInfo({required this.distance, required this.isNear});
}
// HostApi: metodi implementati nel codice nativo, chiamati da Dart
@HostApi()
abstract class ProximityHostApi {
bool isAvailable();
ProximityInfo getCurrentReading();
}
// FlutterApi: metodi implementati in Dart, chiamati dal nativo
@FlutterApi()
abstract class ProximityFlutterApi {
void onProximityChanged(ProximityInfo info);
}
// Genera il codice:
// flutter pub run pigeon --input pigeons/proximity.dart
플랫폼 채널과 기존 플러그인을 사용해야 하는 경우
- 플랫폼 채널을 작성하기 전에: pub.dev를 검색하여 플러그인이 이미 존재하는지 확인하세요.
- pub.dev에는 40,000개 이상의 패키지가 있습니다. 대부분의 기본 API가 이미 포함되어 있습니다.
- 플랫폼 채널 사용: 기업 독점 SDK, 매우 구체적인 API, 기존 기본 레거시 코드와의 통합
- 새로운 공유 가능한 플러그인의 경우: 유형 안전성과 유지 관리 감소를 위해 Pigeon을 사용하세요.
결론
플랫폼 채널은 Flutter를 진정으로 보편적으로 만드는 메커니즘입니다. 모든 기본 API이며 MethodChannel 또는 EventChannel을 통해 연결할 수 있습니다. 비둘기 유형 안전성을 추가하고 상용구의 대부분을 제거합니다. 핵심은 좋은 기본 통합과 오류를 적절하게 처리 PlatformException 및 리소스 정리(리스너 등록 취소, 스트림 취소) 올바른 라이프사이클에서.







