Flutter與原生通信 - Flutter Plugin

本文主要介紹Flutter Plugin原理以及其開發和發佈過程。html

前言

Flutter優點主要體如今UI上--高性能且跨平臺表現一致。可是針對平臺(Android、IOS)的實現,如:獲取電量、判斷WiFi使用、調起WebView加載網頁等,得調用特定平臺的API包。Flutter Plugin就是爲調用平臺API而生。前端

下文中所說起到的"平臺"指的是Android、IOS兩端。android

介紹

Flutter Plugin

包含針對Android(Java或Kotlin代碼)或iOS(Objective-C或Swift代碼)的原生實現,經過Platform Channels與FLutter(dart)層通信並暴露API。ios

Platform Channels

  • 容許Flutter UI和平臺之間傳遞消息。
  • Platform Channel中的消息和響應是異步傳遞的,以確保用戶界面保持響應。
  • 一個Flutter應用能夠存在多個channel,使用name做爲區分。
  • Platform Channel並不是是線程安全,故平臺跟Flutter Engine的全部交互必須在平臺的主線程中執行的。
  • Flutter默認的消息編解碼器是StandardMessageCodec class,現支持平臺數據類型以下:

建立Flutter Plugin

接下來介紹使用Android Studio建立Flutter Plugin。使用Visual Studio Code建立的過程也是大同小異,機智的你必定能觸類旁通,在這裏就不一一細說。swift

第一步:選擇建立一個Flutter project

第二步:選擇Flutter Project的類型爲Flutter Plugin

  • Flutter Application 是建立一個純flutter項目工程
  • Flutter Plugin 是建立一個能夠暴露平臺API的插件工程
  • Flutter Package 是建立一個純Dart的組件包。
  • Fultter Module 是建立一個Flutter Module,用於被引入現有的原生App。

第三步:填寫Project name等工程信息,完成Flutter Plugin建立。

工程建立完成後的目錄結構以下:api

  • lib/flutter_plugin_eg.dart 是插件包dart API實現
  • android/src/main/ 是插件包API Android的實現
  • ios/Classes/ 是插件包API IOS的實現
  • example 是基於依賴當前插件的純flutter示例工程,通常用做展現API調用。

注:從 Flutter 1.9 開始,iOS 新項目默認使用 Swift 語言,而非 Objective-C;Android 新項目則默認使用 Kotlin,而非 Java。若有須要,是可隨時切換回以前的 Objective-C 或 Java。 安全

使用Android模擬器運行example工程

咱們先看看示例工程在Android模擬器上的運行效果:異步

進入example工程目錄,運行lib/main.dart。async

APP在Android模擬器運行後,能夠看到屏幕出現了"Running on: Android 9"。ide

接下來咱們經過代碼看看flutter是怎麼顯示出當前平臺的系統版本。

示例工程 example/lib/main.dart

關鍵代碼:

...
import 'package:flutter_plugin_eg/flutter_plugin_eg.dart';
...
platformVersion = await FlutterPluginEg.platformVersion;
...
setState(() {
  _platformVersion = platformVersion;
});
...
body: Center(
  child: Text('Running on: $_platformVersion\n'),
)
複製代碼

示例工程中的lib/main.dart引入了咱們剛剛建立Flutter Plugin中dart API實現flutter_plugin_eg.dart

隨後使用異步的方式調用flutter_plugin_eg.dartFlutterPluginEg.platformVersion並把返回值賦值給platformVersion,隨後經過setState方法把platformVersion的值賦值給當前狀態組件的_platformVersion,觸發UI重渲把_platformVersion的值"Android 9"顯示出來。

Flutter Plugin中dart API實現 lib/flutter_plugin_eg.dart

全量代碼:

import 'dart:async';

import 'package:flutter/services.dart';

class FlutterPluginEg {
  static const MethodChannel _channel =
      const MethodChannel('flutter_plugin_eg');

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

複製代碼
  • service.dart暴露與平臺通信的API,如:MethodChannelPlatform Channel的一種類型
  • _channel是FlutterPluginEg類的屬性,是一個實例化的MethodChannel,name爲"flutter_plugin_eg"
  • platformVersion是FlutterPluginEg類的靜態可計算屬性,會異步返還一個String。
  • platformVersion中,調用_channelinvokeMethod方法,入參"getPlatformVersion"爲調用平臺約定的方法名。而後把invokeMethod的異步結果賦值給String version做爲platformVersion的返回值。

Flutter Plugin中Android實現

android/src/main/kotlin/.../FlutterPluginEgPlugin.kt 全量代碼:

package com.yy.flutter_plugin_eg

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
import io.flutter.plugin.common.PluginRegistry.Registrar

class FlutterPluginEgPlugin: MethodCallHandler {
  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "flutter_plugin_eg")
      channel.setMethodCallHandler(FlutterPluginEgPlugin())
    }
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else {
      result.notImplemented()
    }
  }
}

複製代碼

再次強調:一個Flutter應用是能夠有多個channel,並而每一個channel均可以有多個method,因此須要重點了解平臺的代碼是經過怎麼樣去對接channel name 與method name。從上文.kt源碼能夠看到:

  • 註冊MethodChannel約定通道名"flutter_plugin_eg",並開始監聽通道消息。
companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "flutter_plugin_eg")
      channel.setMethodCallHandler(FlutterPluginEgPlugin())
    }
  }
複製代碼
  • 實現onMethodCall方法,判斷方法名"getPlatformVersion",返回Android ${android.os.Build.VERSION.RELEASE}
override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else {
      result.notImplemented()
    }
  }
複製代碼

當channel name 和 method name 約定後,Flutter Plugin就能夠在dart中方便調用平臺的實現,並暴露API供Flutter項目使用。

Flutter Plugin中IOS實現

ios/Classes/SwiftFlutterPluginEgPlugin.swift

import Flutter
import UIKit

public class SwiftFlutterPluginEgPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "flutter_plugin_eg", binaryMessenger: registrar.messenger())
    let instance = SwiftFlutterPluginEgPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    result("iOS " + UIDevice.current.systemVersion)
  }
}
複製代碼

能夠看出,IOS與Android實現思路雷同:經過"flutter_plugin_eg"註冊FlutterMethodChannel並開始監聽。

但驚奇發現Flutter Plugin中swift默認生成的代碼竟然沒FlutterMethodCall方法名判斷,在swift斷點看是能獲取到"getPlatformVersion"這個方法名的。

IOS模擬器上運行效果:

發佈Flutter Plugin

輪子造好後,能夠發佈到pub.dev,如下內容純屬搬磚。

補充文檔

在Flutter Plugin根目錄添加

  • README.md:介紹包的文件
  • CHANGELOG.md 記錄每一個版本中的更改
  • LICENSE 包含軟件包許可條款的文件
  • 編寫API文檔

發佈package

一旦你實現了一個包,你能夠在Pub上發佈它 ,這樣其餘開發人員就能夠輕鬆使用它

在發佈以前,檢查pubspec.yaml、README.md以及CHANGELOG.md文件,以確保其內容的完整性和正確性。

而後, 運行 dry-run 命令以查看是否都準備OK了:

flutter packages pub publish --dry-run
複製代碼

最後, 運行發佈命令:

flutter packages pub publish
複製代碼

有關發佈的詳細信息,請參閱Pub publishing docs

思考

開發一個功能完備的APP,說到底仍是須要有原平生臺開發的能力,就如Flutter Plugin須要開發原平生臺部分。相信有不少Web前端童鞋也開始接觸Flutter,可是若是不去深刻了解原生開發,那麼也只能停留在UI層上。

做者

Doerme

相關文章
相關標籤/搜索