일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Swift flutterviewcontroller
- Flutter UIKitView MethodChannel
- swift autolayout
- 스위프트 앨범
- 안드로이드 에러
- Swift flutterview
- 플러터 뷰 컨트롤러
- 앱 백그라운드 푸시 데이터 저장
- NotificationService Extension
- 스위프트
- 안드로이드 FCM
- 스위프트 카메라
- native flutter view
- 안드로이드 앨범
- 스위프트 푸시
- 앱 꺼졌을 때 푸시 데이터 저장
- swift sms
- swift 문자
- 스위프트 UserDefaults
- flutter rotate
- 노티피케이션 익스텐션
- 안드로이드 숏컷
- silent push
- 스위프트 웹뷰
- 스위프트 테이블 뷰 셀
- FlutterView MethodChannel
- Flutter NativeView
- 푸시 데이터 저장
- flutter 회전
- 안드로이드 바로가기
- Today
- Total
Things take time
[Flutter] Flutter와 Native(iOS)간의 통신 방법 - dynamic code 본문
기존 글
https://g-y-e-o-m.tistory.com/184
이 글의 경우엔, AppDelegate에서의 역할이 컸다.
각각의 플러터 플랫폼 뷰를 상속받는 클래스를 연결하고, 메서드 채널을 연결하는 방식의 기초 방법이었는데 이 방법을 적용하니 문제가 발생되었다. 내 경우엔 플러터에서 네이티브 뷰를 UIKitView로 불러와 사용하지만, 다른 플러터 화면에서도 똑같은 뷰를 재사용해야 했다. 다만 전달하는 파라미터만 달라질 뿐이었기 때문에 생각해볼 건 두 가지였다.
1. 사용하는 만큼의 NativeView를 생성한다.
위의 기존 글을 바탕으로 설명한다. GyeomFactory라는 Class를 다른이름으로 생성하는 방법이다. 이런 경우에는 필요한 만큼의 플러터플랫폼 뷰 팩토리를 상속받는 클래스를 계속 생성해야한다.
-> 즉, 똑같은 뷰를 재사용해야하는데 그만큼을 생성한다는 것 자체가 비효율적이다. 물론 구현은 된다.
2. 기존의 GyeomFactory를 재사용한다.
글에서 적은 내용처럼 가장 알맞다. 다만, 이 경우에는 플러터에서 뷰가 스택으로 쌓일 경우 문제가 발생할 수 있다.
예를 들어, A라는 라우트(페이지)에서 B라우트를 띄웠을 때(A를 remove하지 않고) A->B라는 라우트가 쌓여있을 것이다. 근데 A에서도 UIKitView를, B에서도 UIKitView를 공통적으로 사용한다고 가정하자. 그리고 각 UIKitView와 플러터 간의 통신을 위해 메서드 채널도 사용해야한다.
기본 메서드 채널에 대해 알아보려면, 메서드 채널 튜토리얼을 보면 이해가 될 것이다.
https://flutter-ko.dev/docs/development/platform-integration/platform-channels
튜토리얼에서는 AppDelegate에서 메서드 채널을 생성하여 사용하고 있다.
이 튜토리얼과 내가 올린 글을 접합하게 되면, GyeomFactory라는 클래스를 연결하는 작업과(1), 메서드 채널을 생성하는 작업(2)가 각각 하나씩 있는데, 뷰를 재사용하는 입장에서 메서드 채널을 호출 할 경우 AppDelegate에서는 현재 열린게 B인지, A인지는 모른 상태로 함수를 호출한다. 이런 경우, A/B가 갖고 있는 UIKitView 두개가 이벤트를 호출받아 그에 대한 작업을 수행하는 일이 발생해버린다.
튜토리얼은 튜토리얼인 만큼 그대로 사용하면 이런 일이 발생한다.
원하는 결과는 다음과 같다.
공통적으로 사용하는 네이티브 뷰(GyeomFactory)가 있고, 각 화면에서 파라미터만 다르게 전달할 것이다. 그리고 메서드 채널을 통해 호출할 때는 그 네이티브 뷰는 공통적으로 호출받지 않고 따로 받고싶다.
AppDelegate
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// Setting CustomUIView
weak var pluginRegister = self.registrar(forPlugin: "testPlugin")
let factory = GyeomFactory(messenger: pluginRegister!.messenger())
// testPlugin 이름으로 등록된 단일 플러그인 Context에 factory와 view-type이란 with-id를 등록
pluginRegister?.register(factory, withId: "view-type")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
위의 공식 튜토리얼에서 사용한 메서드 채널을 AppDelegate에서 사용하지 않는 것이 중요하다.
GyeomFactory
/**
FlutterPlatformView를 상속받는 클래스: 실제 플러터 내에 포함될 UIView를 리턴하는 클래스(func view() -> UIView {}가 프로토콜내 필수 구현함수로 구현되어 있다)
FlutterPlatformViewFactory를 상속받는 클래스 : FlutterPlatformView를 상속받아 UIView를 리턴하는 그 클래스를 연결하는 클래스(create()가 프로토콜내 필수 구현함수로 등록되어 있다, createArgsCodec()는 optional)
*/
class GyeomFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger?
private var gyeomView: GyeomView?
override init() {
super.init()
}
init(messenger: FlutterBinaryMessenger) {
super.init()
self.messenger = messenger
}
/**
Only needs to be implemented if `createWithFrame` needs an arguments parameter.
Dart에서 파라미터 전달시 필요
*/
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
let methodChannel = FlutterMethodChannel(name: "method-channel-" + "\(viewId)", binaryMessenger: messenger!)
gyeomView = GyeomView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger,
methodChannel: methodChannel)
return gyeomView ?? GyeomView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger,
methodChannel: methodChannel)
}
}
class GyeomView: NSObject, FlutterPlatformView{
private var containerView: UIView
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger?,
methodChannel: FlutterMethodChannel
) {
containerView = UIView()
super.init()
methodChannel.setMethodCallHandler({
[weak self]
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
guard let self = self else { return }
switch call.method{
case "mute":
result(nil)
break
case "start":
result(nil)
break
case "stop":
result(nil)
break
default:
result(FlutterMethodNotImplemented)
break
}
})
}
func view() -> UIView {
return containerView
}
}
"view-type"이란 값으로 UIKitView를 처음 만들 때 호출되는 함수인 create에서 viewId는 고유 값으로 등록된다. 그러므로 이 값을 통해 MethodChannel을 등록, 실제 그려지는 뷰인 GyeomView에 넘겨서 setMethodCallHandler를 통해 등록하면 되는 것이다.
Flutter
UiKitView(
viewType: 'view-type',
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: (int id) {
print('[Gyeom] onPlatformViewCreated call :: id: $id');
_methodChannel = MethodChannel('method-channel-$id');
},
),
그리고 onPlatfomViewCreated에서 넘겨받는 result의 id가 viewId이므로, 여기서 플러터단의 메서드 채널을 등록하면 이제 플러터와 네이티브 뷰의 연결이 이루어지게 되며, 이 네이티브뷰를 재사용하더라도 처음 UIKitView를 호출하는 시점에 새로운 viewId가 생성되므로 한번의 호출로 여러개의 네이티브 뷰가 호출받게 되는 불상사는 없을 것이다.
'Flutter' 카테고리의 다른 글
[Flutter] 화면을 회전하되, 기존 회전값 유지하고 싶을때 (0) | 2022.08.03 |
---|---|
[Flutter] iOS 프로젝트에 여러 개의 FlutterViewController 넣기 및 MethodChannel 구성하기(Swift) (0) | 2022.05.25 |
[Flutter] iOS 프로젝트에 Flutter 모듈 import 및 FlutterViewController 넣기(import Flutter Module to Swift/add FlutterView) (1) | 2022.05.24 |
[Flutter] 잡다한 플러터 이것저것(계속 수정/추가) (0) | 2022.05.17 |
[Flutter] Flutter안에 Native(iOS)의 UIView 넣는 방법 (0) | 2022.05.17 |