$30 off During Our Annual Pro Sale. View Details »

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

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

Katsumi Onishi

August 28, 2018
Tweet

More Decks by Katsumi Onishi

Other Decks in Technology

Transcript

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

    View Slide

  2. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. Method Channel Implementation

    View Slide

  8. 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

    View Slide

  9. 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();
    }
    }
    }

    View Slide

  10. 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];
    }

    View Slide

  11. 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);
    }

    View Slide

  12. 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;
    }
    }
    }

    View Slide

  13. Event Channel Implementation

    View Slide

  14. 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) {
    }

    View Slide

  15. 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);
    }

    View Slide

  16. 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;
    }

    View Slide

  17. 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);
    }
    }
    }
    };

    View Slide

  18. 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;
    }

    View Slide

  19. 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];
    }

    View Slide

  20. 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;
    }

    View Slide

  21. 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

    View Slide

  22. 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});
    }

    View Slide

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

    View Slide

  24. 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

    View Slide

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


    View Slide

  26. 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()

    View Slide

  27. 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)
    }

    View Slide

  28. 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
    }

    View Slide

  29. 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

    View Slide

  30. 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');

    View Slide

  31. 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);
    }

    View Slide

  32. 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');
    }

    View Slide

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

    View Slide

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

    View Slide