IT기술/플러터 (flutter)

Flutter Method Channel 완벽 가이드: 네이티브 코드와 양방향 통신 구현하기

후스파 2025. 7. 15. 08:41
반응형

플러터(Flutter)는 기본적으로 크로스플랫폼 개발 프레임워크이지만, 플랫폼별(안드로이드, iOS) 고유 기능이나 네이티브 API(예: 센서, 배터리, 서드파티 SDK 등)와 연동이 필요할 때가 많습니다. Method Channel을 사용하면 Dart 코드와 네이티브 코드(Java/Kotlin, Swift/Objective-C) 간의 양방향 통신이 가능합니다.


Method Channel의 구조와 원리

Method Channel은 Dart(Flutter)와 각 플랫폼의 네이티브 코드 사이에 메시지를 주고받는 비동기 통신 채널입니다.

  • 각 채널은 고유한 이름(String)으로 식별되며, Dart에서 메서드 호출 → 네이티브에서 처리 → 결과 반환의 구조로 동작
  • 데이터는 표준 메시지 코덱(StandardMessageCodec)으로 직렬화되어 전달되며, bool, int, double, String, List, Map 등 다양한 타입을 지원

구현 방법

Flutter(Dart) 측: 채널 선언 및 메서드 호출

import 'package:flutter/services.dart';

class PlatformService {
  static const platform = MethodChannel('com.example.app/channel');

  Future getPlatformVersion() async {
    try {
      final String version = await platform.invokeMethod('getPlatformVersion');
      return version;
    } on PlatformException catch (e) {
      return "Failed: ${e.message}";
    }
  }
}
  • MethodChannel 생성 시 채널명은 네이티브 코드와 반드시 일치해야 합니다
  • invokeMethod('메서드이름', arguments)로 네이티브 메서드 호출, 결과는 Future로 반환

Android(Java/Kotlin) 측: 메서드 핸들러 구현

Kotlin 예시

class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example.app/channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
            call, result ->
            when (call.method) {
                "getPlatformVersion" -> {
                    val version = android.os.Build.VERSION.RELEASE
                    result.success(version)
                }
                else -> result.notImplemented()
            }
        }
    }
}

setMethodCallHandler에서 메서드명 분기, 결과는 result.success/ error/ notImplemented로 반환

iOS(Swift) 측: 메서드 핸들러 구현

Swift 예시

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.app/channel", binaryMessenger: controller.binaryMessenger)
    channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "getPlatformVersion" {
        result(UIDevice.current.systemVersion)
      } else {
        result(FlutterMethodNotImplemented)
      }
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

채널명과 메서드명이 Dart와 일치해야 하며, 결과는 result()로 반환


데이터 타입 매핑

bool, int, double, String, List, Map 등 Dart의 주요 타입은 각 네이티브 언어의 대응 타입으로 자동 변환됩니다.
예: Dart의 int → Kotlin의 Int/Long, Swift의 NSNumber, Java의 Integer/Long 등


네이티브 → Flutter 호출 (역방향)

네이티브 코드에서 invokeMethod를 사용해 Flutter로 이벤트나 데이터를 전달할 수도 있습니다.
Flutter에서는 setMethodCallHandler로 네이티브에서 오는 호출을 처리합니다.


실전 활용 팁

핵심 개발 포인트

  • 채널명, 메서드명 일치: Dart와 네이티브 코드 모두 동일한 채널명/메서드명을 사용해야 합니다
  • 비동기 처리: 모든 통신은 Future 기반 비동기 방식이므로, UI 블로킹 없이 안전하게 동작합니다
  • 데이터 직렬화: 복잡한 데이터는 Map/List로 변환해 주고받는 것이 안전합니다
  • Pigeon 패키지: 타입 안전성과 자동 코드 생성을 원한다면 Pigeon을 활용할 수 있습니다

대표 활용 예시

배터리 잔량, 센서 정보, 네이티브 SDK, 서드파티 결제, 푸시 알림, 파일 시스템 접근 등 Flutter에서 직접 지원하지 않는 플랫폼 기능 연동에 필수입니다.

배터리 정보 조회 예시

// Flutter 측
Future getBatteryLevel() async {
  try {
    final int result = await platform.invokeMethod('getBatteryLevel');
    return result;
  } on PlatformException catch (e) {
    return -1;
  }
}
// Android 측
"getBatteryLevel" -> {
    val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
    val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    result.success(batteryLevel)
}

마무리

플러터의 Method Channel을 활용하면 안드로이드(Java/Kotlin), iOS(Swift/Obj-C)의 네이티브 기능을 자유롭게 호출하고, 플랫폼별 맞춤 기능을 하나의 코드베이스에서 효율적으로 확장할 수 있습니다.
크로스플랫폼의 장점을 유지하면서도 각 플랫폼의 고유한 기능을 최대한 활용할 수 있는 Method Channel을 통해 더욱 완성도 높은 Flutter 앱을 개발해보세요.

반응형