iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
Mobile Development

Flutter - 從 Packages & Plugins 掌握原生系列 第 14

Day14 開發套件 - 範例程式碼介紹03 iOS 端

  • 分享至 

  • xImage
  •  

最後來看Native 端(iOS):

補充:iOS 中的 .h 和.m 檔

.h 為標頭檔,做為宣告屬性及方法使用

.m 為Objective-C檔,做為實際編寫屬性值及方法內容使用

這邊先介紹一下,Flutter 專案的iOS 入口點在AppDelegate,其繼承自Flutter.frameworkFlutterAppDelegate

@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

在啟動時進入AppDelegate後會執行GeneratedPluginRegistrant.register(with: self),而GeneratedPluginRegistrant中的 (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry會實現當執行flutter pub get時生成的Flutter 所依賴iOS 端的插件

舉例來說,我們來看之前建立plugin 時自動產生的example(使用我們plugin package 的範例專案),此範例專案已經依賴我們的plugin package,我們就可以看到它的GeneratedPluginRegistrant.m

#import "GeneratedPluginRegistrant.h"

#if __has_include(<batterylevel/BatterylevelPlugin.h>)
#import <batterylevel/BatterylevelPlugin.h>
#else
@import batterylevel;
#endif

@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [BatterylevelPlugin registerWithRegistrar:[registry registrarForPlugin:@"BatterylevelPlugin"]];
}

@end

使範例專案在啟動iOS 時,會註冊Flutter 依賴的iOS 端插件(此時會呼叫我們的pluginBatterylevelPlugin來註冊)

回到我們的plugin,我們來看看如何實現Native 端(iOS)的功能:

預設建立的檔案主要有:

  • ios/Classes/BatterylevelPlugin

    #import "BatterylevelPlugin.h"
    #if __has_include(<batterylevel/batterylevel-Swift.h>)
    #import <batterylevel/batterylevel-Swift.h>
    #else
    // Support project import fallback if the generated compatibility header
    // is not copied when this plugin is created as a library.
    // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
    #import "batterylevel-Swift.h"
    #endif
    
    @implementation BatterylevelPlugin
    + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
      [SwiftBatterylevelPlugin registerWithRegistrar:registrar];
    }
    @end
    

    當別人使用我們的plugin 時,在啟動iOS 後會註冊它們Flutter 所依賴的iOS 端插件,我們的plugin 就透過BatterylevelPlugin提供一個SwiftBatterylevelPlugin來註冊,其plugin 的功能也會在SwiftBatterylevelPlugin來實現

  • ios/Classes/SwiftBatterylevelPlugin.swift

    用來實現註冊方法以及我們插件的功能,其中FlutterPlugin為插件協議,所以必須實作註冊方法

    public class SwiftBatterylevelPlugin: NSObject, FlutterPlugin {
      public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "batterylevel", binaryMessenger: registrar.messenger())
        let instance = SwiftBatterylevelPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
      }
    
      public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        result("iOS " + UIDevice.current.systemVersion)
      }
    }
    

    在註冊方法中需要新建用來通信的Channel(對應Flutter 端的MethodChannel,iOS 端為FlutterMethodChannel)並與FlutterPluginRegistrarFlutterBinaryMessenger進行連結綁定,再透過調用addMethodCallDelegate方法將Channel註冊到FlutterEngine裡,這邊我們來看看FlutterEngine 裡的原始碼:

    - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
                          channel:(FlutterMethodChannel*)channel {
      [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        [delegate handleMethodCall:call result:result];
      }];
    }
    

    這邊將Channel透過setMessageHandlerFlutterBinaryMessenger 註冊一個FlutterMethodChannel用的FlutterMethodCallHandler來處理接收訊息,當Flutter 端通過invokeMethod調用Native iOS 端方法時,就會通過傳進來的FlutterPlugin 的delegate將訊息回調給FlutterPlugin(這邊的SwiftBatterylevelPlugin)的handleMethodCall方法

    回到SwiftBatterylevelPlugin,最後handle用來實現FlutterPlugin- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result,這邊產生的範例程式直接回傳版本資訊給Flutter 端

    而範例程式並無針對Flutter 端呼叫FlutterMethodCall的不同方法名做判斷處理,我們來對其做以下修改:

        public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
            switch call.method {
            case "getPlatformVersion":
                result("iOS " + UIDevice.current.systemVersion)
            default:
                result(FlutterMethodNotImplemented)
            }
        }
    

在看完plugin 範例程式的版本資訊功能後,我們對plugin 對Channel功能的實現已經有初步的了解,這邊就先以一樣的步驟,新增取得batterylevel 電池電量資訊的功能吧


上一篇
Day13 開發套件 - 範例程式碼介紹02 Android 端
下一篇
Day15 開發套件 - 實作MethodChannel
系列文
Flutter - 從 Packages & Plugins 掌握原生30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言