728x90
728x90
SMALL
Package와 Plugin의 차이점
해당 글은 Flutter에서 기본적으로 제공하는 플랫폼 버전을 가져오는 예제를 이용해 작성하였다.
실제 제작한 Plugin 프로젝트는 Flutter와 iOS/Android의 통신을 위한 Plugin만 사용된 것이 아니라,
부가적인 추가과정이 많아 Flutter Plugin 프로젝트로만 예제로 쓰기에 복잡했다 ㅜㅜ
예를 들어 iOS는 .a 확장자인 라이브러리를 이용해 추가적으로 C언어로 구현된 함수 사용이 필요했으며,
Android는 C++언어로 작성된 코드와의 통신을 위해 JNI를 구현하는 추가과정이 필요했다...
그래서 기본예제로 글을 작성할 수 밖에 없었으나 이후 추가 함수라도 구현하여 글을 수정해볼까 한다!
Plugin 프로젝트 생성
새 프로젝트 생성 - Flutter
Project Type을 Plugin으로 설정
나머지 항목은 기존 프로젝트와 같이 채우기
Platform은 제작할 Plugin이 사용될 플랫폼들만 선택하기
728x90
MethodChannel을 이용한 통신
Method Channel을 이용한 통신 구조 도식화
프로젝트 생성 후 lib파일을 확인하면 3개의 .dart 파일이 생성되어있다.
- test_plugin_platform_interface.dart
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'test_plugin_method_channel.dart';
/// 인터페이스 작성
abstract class TestPluginPlatform extends PlatformInterface {
/// TestPluginPlatform 생성
TestPluginPlatform() : super(token: _token);
static final Object _token = Object();
/// TestPluginPlatform의 인스턴스
static TestPluginPlatform _instance = MethodChannelTestPlugin();
static TestPluginPlatform get instance => _instance;
/// 플랫폼별 구현체는 자신들을 등록할 때 TestPluginPlatform를 확장한 클래스로 값을 설정해야함
static set instance(TestPluginPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// 함수가 구현되지 않았을 경우 처리
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}
PlatformInterface라는 추상 클래스를 확장하여 작성된 커스텀 추상 클래스(TestPluginPlatform)를 작성한다
- test_plugin_method_channel.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'test_plugin_platform_interface.dart';
/// TestPluginPlatform 구현
/// Method Channel을 사용
class MethodChannelTestPlugin extends TestPluginPlatform {
/// 네이티브와 상호 작용하는 데 사용되는 메소드 채널
@visibleForTesting
final methodChannel = const MethodChannel('test_plugin');
/// 특정 네이티브 함수를 호출하여 결과를 얻어온다
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}
커스텀 추상 클래스(TestPluginPlatform)를 구현하는 클래스(MethodChannelTestPlugin)를 작성한다
해당 클래스 내부에 MethodChannel를 생성하여 네이티브 코드들과 직접적인 통신을 할 수 있다
- test_plugin.dart
import 'test_plugin_platform_interface.dart';
/// Flutter에서 실제 호출되는 함수
/// 함수 내부에서 TestPluginPlatform의 인스턴스를 이용해 Method Channel로 접근한다
class TestPlugin {
Future<String?> getPlatformVersion() {
return TestPluginPlatform.instance.getPlatformVersion();
}
}
위에서 작성된 커스텀 추상 클래스(TestPluginPlatform) 내부의 정적 인스턴스를 이용한다
인스턴스의 특정 함수를 호출하면 TestPluginPlatform를 구현한 MethodChannelTestPlugin 클래스의 메소드가 호출되며,
해당 메소드 내부에서 Method Channel을 사용하여 네이티브 코드와 통신한다
플랫폼(Android, iOS)별 코드 작성
- Android
package com.bailey.testplugin.test_plugin
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
// Flutter와 네이티브 안드로이드 간 통신을 담당하는 MethodChannel
class TestPlugin: FlutterPlugin, MethodCallHandler {
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "test_plugin")
channel.setMethodCallHandler(this)
}
// methodChannel.invokeMethod<String>('getPlatformVersion');
// 호출 시 작동하며 call.method = 'getPlatformVersion'로 전달된다
// 해당 변수값을 통해 구분 및 작동 시킬 수 있다
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
- iOS
import Flutter
import UIKit
// Flutter와 네이티브 iOS 간 통신을 담당하는 MethodChannel (FlutterMethodChannel)
public class TestPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "test_plugin", binaryMessenger: registrar.messenger())
let instance = TestPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
// methodChannel.invokeMethod<String>('getPlatformVersion');
// 호출 시 작동하며 call.method = 'getPlatformVersion'로 전달된다
// 해당 변수값을 통해 구분 및 작동 시킬 수 있다
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}
Flutter에서 "함수이름"을 인자로 사용해 메소드를 호출하면 onMethodCall 메소드가 실행되며
call.method 변수에 "함수이름"을 받아온다.
해당 변수를 이용하여 네이티브 코드(Android/iOS)에서 실행시켜야하는 코드를 실행하거나,
결과 값을 Flutter로 리턴하여 통신을 완료한다.
플랫폼에 따른 데이터 유형
네이티브 코드 내에서 실행만 진행하는 함수 호출은 상관없지만
네이티브에서 특정 정보를 받아 Flutter로 전달 해야한다면 데이터 타입에 대한 부분이 중요하다.
아래 URL에는 플랫폼 별로 지원하는 데이터 유형 및 코덱이 작성되어 있으며 아래 간단하게 Kotlin과 Swift만 첨부하였다.
참고
728x90
728x90
LIST