Flutter package 插件開(kāi)發(fā)

一.Package類型

  • Dart packages:用Dart編寫(xiě)的通用包,其中一些可能包含Flutter特定的功能,因此依賴于Flutter框架,僅可用于Flutter,例如fluro包。
  • Plugin packages:一種專用的Dart包,其中包含一個(gè)用Dart代碼編寫(xiě)的API以及一個(gè)或多個(gè)特定于平臺(tái)的實(shí)現(xiàn),例如url_launcher包。

二.開(kāi)發(fā)Dart packages

1.創(chuàng)建包

flutter create --template=package hello

2.直接在hello/lib下進(jìn)行flutter平臺(tái)的代碼開(kāi)發(fā)。

三.開(kāi)發(fā)Plugin packages

1.創(chuàng)建包

--platforms=后面表示插件支持的平臺(tái),可用的平臺(tái)有:android、iosweb、linuxmacoswindows。

--org用于生成插件的包標(biāo)識(shí)符。

-a表示android支持的語(yǔ)言,默認(rèn)為kotlin

-i表示ios支持的語(yǔ)言,默認(rèn)為swift。

flutter create --org com.example --template=plugin --platforms=android,ios -i swift -a kotlin hello

2.添加ios平臺(tái)代碼

<1>確保代碼至少已經(jīng)構(gòu)建過(guò)一次:
cd hello/example
flutter clean
flutter build ios --no-codesign
<2>啟動(dòng)Xcode:hello/example/ios/Runner.xcworkspace,開(kāi)始編輯ios平臺(tái)代碼。
<3>添加第三方依賴Pods/Development Pods/hello/Pod/hello.podspec:
Pod::Spec.new do |s|
  # lines skipped
  s.dependency 'MTBBarcodeScanner'

添加完成后pod install

<4>插件代碼位于Pods/Development Pods/hello/../../example/ios/.symlinks/plugins/hello/ios/Classes下。

遵守FlutterPlatformViewFactory協(xié)議的文件:

import UIKit

class BarcodeScannerFactory: NSObject, FlutterPlatformViewFactory {
    private var messenger: FlutterBinaryMessenger?
    
    init(messenger: FlutterBinaryMessenger) {
        super.init()
        self.messenger = messenger
    }
    
    func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
        /// 注:frame和viewId獲取不到,frame為zero,viewId為0
        return BarcodeScannerView(
            frame: frame,
            viewIdentifier: viewId,
            arguments: args,
            binaryMessenger: messenger
        )
    }
    
    func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
        return FlutterStandardMessageCodec(readerWriter: FlutterStandardReaderWriter())
    }
}

遵守FlutterPlatformView協(xié)議的文件:

import UIKit
import MTBBarcodeScanner

class BarcodeScannerView: NSObject, FlutterPlatformView {
    private var scannerView: UIView!
    private var scanner: MTBBarcodeScanner!
    
    init(
        frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?,
        binaryMessenger messenger: FlutterBinaryMessenger?
    ) {
        super.init()
        scannerView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        scanner = MTBBarcodeScanner(previewView: scannerView)
        do {
            try scanner.startScanning(resultBlock: { _ in })
        } catch {}
    }
    
    func view() -> UIView {
        return scannerView;
    }
}

在插件中注冊(cè)FlutterPlatformViewFactory

import Flutter
import UIKit

class BarcodeScannerPlugin: NSObject, FlutterPlugin {
    public static func register(with registrar: FlutterPluginRegistrar) {
        registrar.register(BarcodeScannerFactory(messenger: registrar.messenger()), withId: "view_type_id_scanner_view")
    }
}

3.添加android平臺(tái)代碼

<1>運(yùn)行example文件代碼

構(gòu)建依賴:

cd hello/example
flutter pub get

Android Studio->File->Open,選擇hello/example/android/build.gradle啟動(dòng)并運(yùn)行。

<2>啟動(dòng)Android Studio:hello/android,開(kāi)始編輯android平臺(tái)代碼。
<3>添加第三方依賴Gradle Scripts/build.gradle(Module:android.hello):
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    /// 添加第三方庫(kù)
    implementation 'me.dm7.barcodescanner:zxing:1.9.13'
}

<4>插件代碼位于hello/java/下。

若出現(xiàn)Unresolved reference: io.flutter,選擇File->Invalidate Caches/Restart...

繼承混合類PlatformViewFactory的文件:

import android.content.Context
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory

class BarcodeScannerFactory(private var messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        return BarcodeScannerView(messenger, context, viewId, args)
    }
}

繼承混合類PlatformView的文件:

import android.content.Context
import android.view.View
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.platform.PlatformView
import me.dm7.barcodescanner.zxing.ZXingScannerView

class BarcodeScannerView(binaryMessenger: BinaryMessenger, context: Context, viewId: Int, args: Any?) : PlatformView {
    private var scannerView = ZXingScannerView(context)

    init {
        scannerView.startCamera()
    }

    override fun getView(): View {
        return scannerView
    }

    override fun dispose() {}
}

在插件中注冊(cè)PlatformViewFactory

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    flutterPluginBinding.platformViewRegistry.registerViewFactory("view_type_id_scanner_view", BarcodeScannerFactory(flutterPluginBinding.binaryMessenger))
  }

4.添加flutter平臺(tái)代碼

代碼位于hello/lib:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class BarcodeScanner extends StatelessWidget {
  const BarcodeScanner({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const String viewType = 'view_type_id_scanner_view';
    const Map<String, dynamic> creationParams = {'name': '張三'};

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return const AndroidView(
          viewType: viewType,
          creationParams: creationParams,
          creationParamsCodec: StandardMessageCodec(),
        );
      case TargetPlatform.iOS:
        return const UiKitView(
          viewType: viewType,
          creationParams: creationParams,
          creationParamsCodec: StandardMessageCodec(),
        );
      default:
        return const Text('暫不支持該平臺(tái)');
    }
  }
}

四.插件發(fā)布

1.完善pubspec.yaml文件

name: 插件名稱  
description: 插件描述  
version: 插件版本號(hào),0.0.1 
author: 作者,xxxx<xx@xxx.com>  
homepage: 項(xiàng)目主頁(yè)地址  
publish_to: 填寫(xiě)私有服務(wù)器的地址(如果是發(fā)布到flutter pub則不用填寫(xiě),插件默認(rèn)是上傳到flutter pub)

2.檢驗(yàn)是否可以發(fā)布

flutter packages pub publish --dry-run 

--dry-run 參數(shù)表示本次執(zhí)行會(huì)檢查插件的配置信息是否有效,插件是否滿足上傳條件。如果成功的話并不會(huì)真正的將插件上傳,而是會(huì)顯示本次要發(fā)布插件的信息,并提示成功。一般在插件的正式發(fā)布前,建議先執(zhí)行該命令,避免在上傳過(guò)程中出現(xiàn)錯(cuò)誤。

3.正式發(fā)布

發(fā)布到pub平臺(tái):

flutter packages pub publish 

發(fā)布到私有服務(wù)器:

flutter packages pub publish --server $服務(wù)器地址  

pubspec.yaml文件中列出的包作者與授權(quán)發(fā)布該包的人員列表不同。發(fā)布某個(gè)軟件包的第一個(gè)版本的人自動(dòng)成為第一個(gè)也是唯一一個(gè)有權(quán)上傳其他版本軟件包的人。要允許或禁止其他人上載版本,請(qǐng)使用pub uploader命令。

五.iOSpodspec文件

1.Podfilepodspec的區(qū)別

  • Podfile指的是將本地或遠(yuǎn)端的庫(kù)導(dǎo)入到該工程。
  • podspec指的是依賴于該庫(kù),若該庫(kù)在本地沒(méi)有,會(huì)先去cocoapods下載導(dǎo)入到該工程。

2.podsepc依賴于git庫(kù)

podsepc文件中的s.dependency只能依賴于cocoapods中的版本,而不能依賴于git庫(kù)??墒褂靡韵路椒ń鉀Q。

事例:依賴于IMKitgit庫(kù)https://github.com/imkit/imkit-ios-framework-v3.git

<1>在插件工程的podsepc文件中依賴:

s.dependency 'IMKit'

<2>在項(xiàng)目工程的Podfile文件中導(dǎo)入:

target 'Runner' do
  use_frameworks!
  use_modular_headers!
  
  pod 'IMKit', :git => 'https://github.com/imkit/imkit-ios-framework-v3.git'
end

3.podsepc依賴于cocoapods的靜態(tài)framework庫(kù)

事例:依賴于靜態(tài)庫(kù)PrintSDK.framework

<1>在插件工程的podsepc文件中依賴:

# 使用靜態(tài)庫(kù)
s.static_framework = true
# 依賴第三方的framework
s.dependency 'PrinterSDK'
# 依賴系統(tǒng)庫(kù)(可選)
s.frameworks = 'UIKit', 'SystemConfiguration'
# Xcode里面Build Settings ->Other Linker Flags設(shè)置-ObjC(可選)
s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }

若不設(shè)置s.static_framework = true,會(huì)報(bào)錯(cuò):The 'Pods-Runner' target has transitive dependencies that include statically linked binaries

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容