Slide 1

Slide 1 text

Handling 3rd-Party Apps in a plugin packages @katsummy - Android Engineer

Slide 2

Slide 2 text

Flutter Plugin Package Plugin Package iOS Host FlutterViewController AppDelegate iOS platform APIs 3rd-Party App Android Host FlutterView Activity Android platform APIs 3rd-Party App App Platform Channels

Slide 3

Slide 3 text

App / Package / Plugin app : アプリケーション package : 共有モジュール(Dart) plugin : プラットフォームの実装を持つ共有モジュール

Slide 4

Slide 4 text

Platform Channels プラットフォーム固有のAPIと通信する - Method Channel - Event Channel

Slide 5

Slide 5 text

Method Channel ホストのメソッドを呼び出してプラットフォームのAPIを実行し、結 果を戻り値として受け取ります。 Flutter App iOS/Android Call Method Return Value (Synchronous)

Slide 6

Slide 6 text

Event Channel ブロードキャストストームの受信者をホストに登録し、コールバッ クで非同期にプラットフォームイベントを受信します。 Flutter App iOS/Android BroadcastStream Listener Event (Asynchronous) Event

Slide 7

Slide 7 text

Method Channel Implementation

Slide 8

Slide 8 text

Method Channel - Android Implement (1/2) public class HelloPlugin implements MethodCallHandler { // static method. public static void registerWith(Registrar registrar) { // 1. MethodChannelを作成 final MethodChannel channel = new MethodChannel(registrar.messenger(), "hello"); // 2. MethodChannelにPluginを登録 HelloPlugin instance = new HelloPlugin(); channel.setMethodCallHandler(instance); } $ flutter create --template=plugin hello

Slide 9

Slide 9 text

Method Channel - Android Implement (2/2) @Override public void onMethodCall(MethodCall call, Result result) { // 3. FlutterからのMethodCallを受け取る if (call.method.equals("getPlatformVersion")) { // 4. Flutterへ結果をコールバックする if (/*condition*/) { // 4-1. Success result.success("Android " + android.os.Build.VERSION.RELEASE); } else { // 4-2. Error result.error("ERROR", "errorMessage", null): } } else { // 4-2. NotImplemented Error result.notImplemented(); } } }

Slide 10

Slide 10 text

Method Channel - iOS Implement (1/2) @interface HelloPlugin : NSObject @end @implementation HelloPlugin // Implemented by the iOS part of a FlutterPlugin. + (void)registerWithRegistrar:(NSObject*)registrar { // 1. MethodChannelを作成 FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"hello" binaryMessenger:[registrar messenger]]; // 2. MethodChannelにPluginを登録 HelloPlugin* instance = [[HelloPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; }

Slide 11

Slide 11 text

Method Channel - iOS Implement (2/2) - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { // 3. FlutterからのMethodCallを受け取る if ([@"getPlatformVersion" isEqualToString:call.method]) { // 4. Flutterへ結果をコールバックする if (/*condition*/) { // 4-1. Success result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); } else { // 4-2. Error result([FlutterError errorWithCode:@"ERROR" message:"errorMessage" details:nil]); } } else { // 4-2. NotImplemented Error result(FlutterMethodNotImplemented); }

Slide 12

Slide 12 text

Method Channel - Flutter Implement class Hello { // 1. MethodChannelを作成 static const MethodChannel _channel = const MethodChannel('hello'); static Future get platformVersion async { // Platform messages may fail, so we use a try/catch PlatformException. try { // 2. メソッドの呼び出し final String version = await _channel.invokeMethod('getPlatformVersion'); return version; } on PlatformException catch (e) { debugPrint(${e}); return null; } } }

Slide 13

Slide 13 text

Event Channel Implementation

Slide 14

Slide 14 text

Event Channel - Android Implement (1/4) public class HelloPlugin implements EventChannel.StreamHandler { // 1. StreamHandlerのメソッドを実装 @Override public void onListen(Object arguments, EventChannel.EventSink events) { } @Override public void onCancel(Object arguments) { }

Slide 15

Slide 15 text

Event Channel - Android Implement (2/4) public class HelloPlugin implements EventChannel.StreamHandler { private EventChannel.EventSink _eventSink; public static void registerWith(Registrar registrar) { // 2. EventChannelの作成 final EventChannel eventChannel = EventChannel(registrar.messenger(), "hello"); // 3. EventChannelにPluginを登録 HelloPlugin instance = new HelloPlugin(); eventChannel.setStreamHandler(instance); }

Slide 16

Slide 16 text

Event Channel - Android Implement (3/4) public class HelloPlugin implements EventChannel.StreamHandler { // 1. StreamHandlerのメソッドを実装 @Override public void onListen(Object arguments, EventChannel.EventSink events) { // 4. イベントストリームを設定 _eventSink = events; } @Override public void onCancel(Object arguments) { _eventSink = null; }

Slide 17

Slide 17 text

Event Channel - Android Implement (4/4) final Runnable runnable = new Runnable() { @Override public void run() { if(_eventSink != null) { // 5. イベントをFlutterにエミット if (/*condition*/) { // 5-1. Success _eventSink.success("Hello"); } else { // 5-2. Error _eventSink.error("ERROR", "errorMessage", null); } } } };

Slide 18

Slide 18 text

Event Channel - iOS Implement (1/4) @interface HelloPlugin : NSObject @end // Implemented by the iOS part of a FlutterPlugin and FlutterStreamHandler. @implementation HelloPlugin // 1. StreamHandlerのメソッドを実装 // イベントストリームの設定する要求を処理 - (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { return nil; } // イベントストリームを破棄する要求を処理 - (FlutterError *)onCancelWithArguments:(id)arguments { return nil; }

Slide 19

Slide 19 text

Event Channel - iOS Implement (2/4) // Implemented by the iOS part of a FlutterPlugin and FlutterStreamHandler. @implementation HelloPlugin + (void)registerWithRegistrar:(NSObject*)registrar { // 2. EventChannelの作成 FlutterEventChannel* channel = [FlutterEventChannel eventChannelWithName:@"hello" binaryMessenger:[registrar messenger]]; // 3. EventChannelにPluginを登録 HelloPlugin* instance = [[HelloPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:methodChannel]; }

Slide 20

Slide 20 text

Event Channel - iOS Implement (3/4) // イベントストリームの設定する要求を処理 - (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { // 4. イベントストリームを設定 _eventSink = eventSink; return nil; } // イベントストリームを破棄する要求を処理 - (FlutterError *)onCancelWithArguments:(id)arguments { _eventSink = nil; return nil; }

Slide 21

Slide 21 text

Event Channel - iOS Implement (4/4) - (void)emit { [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ if(_eventSink != null) { // 5. イベントをFlutterにエミット if (/*condition*/) { // 5-1. Success _eventSink("Hello"); } else { // 5-2. Error _eventSink([FlutterError errorWithCode:@"ERROR" message:"errorMessage" details:nil]); } } }]; } @end

Slide 22

Slide 22 text

Event Channel - Flutter Implement class Hello { // 1. EventChannelの作成 static const EventChannel _channel = const EventChannel('hello'); Hello() { // 2. ブロードキャストストリームのリスナーを設定 _eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError); } // 3. イベントストリームの処理 void _onEvent(Object event) { debugPrint(${event}); } // 4. エラーストリームの処理 void _onError(Object error) { debugPrint(${error}); }

Slide 23

Slide 23 text

How to handle 3rd-Party Apps in a plugin packages

Slide 24

Slide 24 text

Flutter Plugin Package Plugin Package iOS Host FlutterViewController AppDelegate iOS platform APIs 3rd-Party App Android Host FlutterView Activity Android platform APIs 3rd-Party App App Platform Channels

Slide 25

Slide 25 text

3rd-Party App Login App Package Login Requeste Plugin 3rd-Party App startActivityForResult Result Login Result Result

Slide 26

Slide 26 text

Start Activity of 3rd-Party App in Plugin (1/4) class FlutterLineLoginPlugin(registrar: Registrar) : MethodCallHandler, EventChannel.StreamHandler, PluginRegistry.ActivityResultListener { companion object { // static method. @JvmStatic fun registerWith(registrar: Registrar): Unit { FlutterLineLoginPlugin(registrar) } } Registrar + activeContext() + activity() + addActivityResultListener()

Slide 27

Slide 27 text

Start Activity of 3rd-Party App in Plugin (2/4) init { // 1. MethodChannelの作成 val methodChannel = MethodChannel(registrar.messenger(), "net.granoeste/flutter_line_login") // 2. EventChannelの作成 val eventChannel = EventChannel(registrar.messenger(), "net.granoeste/flutter_line_login_result") // 3. MethodChannelとEventChannelにPluginを登録 methodChannel.setMethodCallHandler(this) eventChannel.setStreamHandler(this) // 4. ActivityResultListenerにPluginを登録 registrar.addActivityResultListener(this) }

Slide 28

Slide 28 text

Start Activity of 3rd-Party App in Plugin (3/4) override fun onMethodCall(call: MethodCall, result: Result) { // 5. FlutterからのMethodCallを受け取る when (call.method) { "startLogin" -> { // 6. Activityの起動 val loginIntent = LineLoginApi.getLoginIntent(activity, channelId) activity.startActivityForResult(loginIntent, REQUEST_CODE) // 7. Flutterへは成功を返す result.success(null) } // 8. StreamHandlerのメソッドを実装 override fun onListen(arguments: Any?, events: EventChannel.EventSink) { // 9. EventSinkを保管 _eventSink = events }

Slide 29

Slide 29 text

Start Activity of 3rd-Party App in Plugin (4/4) // 10. onActivityResultを実装 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { val result = LineLoginApi.getLoginResultFromIntent(data) when (result.responseCode) { LineApiResponseCode.SUCCESS -> { // 10. EventSinkに成功を流す eventSink.success(null) } else -> { // 11. EventSinkにエラーを流す eventSink.error(result.responseCode.toString(), result.errorData.message, result.errorData.toString()) } } return true

Slide 30

Slide 30 text

Start Activity of 3rd-Party App in Package (1/3) class FlutterLineLogin { // 1. MethodChannelを作成 static const MethodChannel _methodChannel = const MethodChannel('net.granoeste/flutter_line_login'); // 2. EventChannelの作成 static const EventChannel _eventChannel = const EventChannel('net.granoeste/flutter_line_login_result');

Slide 31

Slide 31 text

Start Activity of 3rd-Party App in Package (2/3) FlutterLineLogin() { // 3. ブロードキャストストリームのリスナーを設定 _eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError); } // 4. イベントストリームの処理 void _onEvent(Object event) { _onLoginSuccess(event); } // 5. エラーストリームの処理 void _onError(Object error) { _onLoginError(error); }

Slide 32

Slide 32 text

Start Activity of 3rd-Party App in Package (3/3) Function _onLoginSuccess; Function _onLoginError; // 6. ログインメソッド startLogin(Function onSuccess, Function onError) async { _onLoginSuccess = onSuccess; _onLoginError = onError; await _methodChannel.invokeMethod('startLogin'); }

Slide 33

Slide 33 text

Start Activity of 3rd-Party App in App (1/3) // 1. インスタンス作成 var _flutterLineLogin = new FlutterLineLogin(); // 2. ログイン await _flutterLineLogin.startLogin( (data) => { // LoginSuccess }, (error) => { // LoginError });

Slide 34

Slide 34 text

flutter_line_login | Flutter Package https://pub.dartlang.org/packages/flutter_line_login FirebaseSignInWithLine https://github.com/granoeste/FirebaseSignInWithLine