做自(zì)由與創造的先行者

Flutter 平台特定的代碼

Flutter開(kāi)發手冊

Flutter使用(yòng)了(le)一個靈活的系統,允許您調用(yòng)特定平台的API,無論在Android上(shàng)的Java或Kotlin代碼中,還是iOS上(shàng)的ObjectiveC或Swift代碼中均可用(yòng)。

Flutter平台特定的API支持不依賴于代碼生成,而是依賴于靈活的消息傳遞的方式:

應用(yòng)的Flutter部分通過平台通道(dào)(platform channel)将消息發送到(dào)其應用(yòng)程序的所在的宿主(iOS或Android)。

宿主監聽的平台通道(dào),并接收該消息。然後它會(huì)調用(yòng)特定于該平台的API(使用(yòng)原生編程語言) - 并将響應發送回客戶端,即應用(yòng)程序的Flutter部分。

框架概述: 平台通道(dào)

使用(yòng)平台通道(dào)在客戶端(Flutter UI)和(hé)宿主(平台)之間傳遞消息,如下(xià)圖所示:

Platform channels architecture

消息和(hé)響應是異步傳遞的,以确保用(yòng)戶界面保持響應(不會(huì)挂起)。

在客戶端,MethodChannel (API)可以發送與方法調用(yòng)相對(duì)應的消息。 在宿主平台上(shàng),MethodChannel 在Android((API) 和(hé) FlutterMethodChannel iOS (API) 可以接收方法調用(yòng)并返回結果。這(zhè)些(xiē)類允許您用(yòng)很(hěn)少的“腳手架”代碼開(kāi)發平台插件。

注意: 如果需要,方法調用(yòng)也(yě)可以反向發送,宿主作(zuò)爲客戶端調用(yòng)Dart中實現(xiàn)的API。 這(zhè)個quick_actions插件就是一個具體的例子

平台通道(dào)數據類型支持和(hé)解碼器

标準平台通道(dào)使用(yòng)标準消息編解碼器,以支持簡單的類似JSON值的高(gāo)效二進制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps(請(qǐng)參閱StandardMessageCodec了(le)解詳細信息)。 當您發送和(hé)接收值時(shí),這(zhè)些(xiē)值在消息中的序列化和(hé)反序列化會(huì)自(zì)動進行。

示例: 使用(yòng)平台通道(dào)調用(yòng)iOS和(hé)Android代碼

以下(xià)演示如何調用(yòng)平台特定的API來(lái)獲取和(hé)顯示當前的電池電量。它通過一個平台消息getBatteryLevel 調用(yòng)Android BatteryManager API和(hé)iOS device.batteryLevel API。 。

該示例在應用(yòng)程序内添加了(le)特定于平台的代碼。如果您想開(kāi)發一個通用(yòng)的平台包,可以在其它應用(yòng)中也(yě)使用(yòng)的話(huà),你(nǐ)需要開(kāi)發一個插件, 則項目創建步驟稍有不同(請(qǐng)參閱開(kāi)發 packages),但(dàn)平台通道(dào)代碼仍以相同方式編寫。

注意: 此示例的完整的可運行源代碼位于:/examples/platform_channel/, 這(zhè)個示例Android是用(yòng)的Java, IOS用(yòng)的是Objective-C,IOS Swift版本請(qǐng)參閱 /examples/platform_channel_swift/

Step 1: 創建一個新的應用(yòng)程序項目

首先創建一個新的應用(yòng)程序:

在終端運行中:flutter create batterylevel

默認情況下(xià),模闆支持使用(yòng)Java編寫Android代碼,或使用(yòng)Objective-C編寫iOS代碼。要使用(yòng)Kotlin或Swift,請(qǐng)使用(yòng)-i和(hé)/或-a标志:

在終端中運行: flutter create -i swift -a kotlin batterylevel

Step 2: 創建Flutter平台客戶端

該應用(yòng)的State類擁有當前的應用(yòng)狀态。我們需要延長這(zhè)一點以保持當前的電量

首先,我們構建通道(dào)。我們使用(yòng)MethodChannel調用(yòng)一個方法來(lái)返回電池電量。

通道(dào)的客戶端和(hé)宿主通過通道(dào)構造函數中傳遞的通道(dào)名稱進行連接。單個應用(yòng)中使用(yòng)的所有通道(dào)名稱必須是唯一的; 我們建議(yì)在通道(dào)名稱前加一個唯一的“域名前綴”,例如samples.flutter.io/battery。

import 'dart:async';

import 'package:flutter/material.dart';

import 'package:flutter/services.dart';

...

class _MyHomePageState extends State {

static const platform = const MethodChannel('samples.flutter.io/battery');

// Get battery level.

}

接下(xià)來(lái),我們調用(yòng)通道(dào)上(shàng)的方法,指定通過字符串标識符調用(yòng)方法getBatteryLevel。 該調用(yòng)可能(néng)失敗 - 例如,如果平台不支持平台API(例如在模拟器中運行時(shí)),所以我們将invokeMethod調用(yòng)包裝在try-catch語句中。

我們使用(yòng)返回的結果,在setState中來(lái)更新用(yòng)戶界面狀态batteryLevel。

// Get battery level.

String _batteryLevel = 'Unknown battery level.';

Future _getBatteryLevel() async {

String batteryLevel;

try {

final int result = await platform.invokeMethod('getBatteryLevel');

batteryLevel = 'Battery level at $result % .';

} on PlatformException catch (e) {

batteryLevel = "Failed to get battery level: '${e.message}'.";

}

setState(() {

_batteryLevel = batteryLevel;

});

}

最後,我們在build創建包含一個小(xiǎo)字體顯示電池狀态和(hé)一個用(yòng)于刷新值的按鈕的用(yòng)戶界面。

@override

Widget build(BuildContext context) {

return new Material(

child: new Center(

child: new Column(

mainAxisAlignment: MainAxisAlignment.spaceEvenly,

children: [

new RaisedButton(

child: new Text('Get Battery Level'),

onPressed: _getBatteryLevel,

),

new Text(_batteryLevel),

],

),

),

);

}

Step 3a: 使用(yòng)Java添加Android平台特定的實現(xiàn)

注意: 以下(xià)步驟使用(yòng)Java。如果您更喜歡Kotlin,請(qǐng)跳到(dào)步驟3b.

首先在Android Studio中打開(kāi)您的Flutter應用(yòng)的Android部分:

啓動 Android Studio

選擇 ‘File > Open…’

定位到(dào)您 Flutter app目錄, 然後選擇裏面的 android文(wén)件夾,點擊 OK

在java目錄下(xià)打開(kāi) MainActivity.java

接下(xià)來(lái),在onCreate裏創建MethodChannel并設置一個MethodCallHandler。确保使用(yòng)與在Flutter客戶端使用(yòng)的通道(dào)名稱相同。

import io.flutter.app.FlutterActivity;

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;

public class MainActivity extends FlutterActivity {

private static final String CHANNEL = "samples.flutter.io/battery";

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(

new MethodCallHandler() {

@Override

public void onMethodCall(MethodCall call, Result result) {

// TODO

}

});

}

}

接下(xià)來(lái),我們添加Java代碼,使用(yòng)Android電池API來(lái)獲取電池電量。此代碼與您在原生Android應用(yòng)中編寫的代碼完全相同。

首先,添加需要導入的依賴。

import android.content.ContextWrapper;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.BatteryManager;

import android.os.Build.VERSION;

import android.os.Build.VERSION_CODES;

import android.os.Bundle;

然後,将下(xià)面的新方法添加到(dào)activity類中的,位于onCreate 方法下(xià)方:

private int getBatteryLevel() {

int batteryLevel = -1;

if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {

BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);

batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);

} else {

Intent intent = new ContextWrapper(getApplicationContext()).

registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /

intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);

}

return batteryLevel;

}

最後,我們完成之前添加的onMethodCall方法。我們需要處理(lǐ)平台方法名爲getBatteryLevel,所以我們在call參數中進行檢測是否爲getBatteryLevel。 這(zhè)個平台方法的實現(xiàn)隻需調用(yòng)我們在前一步中編寫的Android代碼,并使用(yòng)response參數返回成功和(hé)錯誤情況的響應。如果調用(yòng)未知(zhī)的方法,我們也(yě)會(huì)通知(zhī)返回:

@Override

public void onMethodCall(MethodCall call, Result result) {

if (call.method.equals("getBatteryLevel")) {

int batteryLevel = getBatteryLevel();

if (batteryLevel != -1) {

result.success(batteryLevel);

} else {

result.error("UNAVAILABLE", "Battery level not available.", null);

}

} else {

result.notImplemented();

}

}

您現(xiàn)就可以在Android上(shàng)運行該應用(yòng)程序。如果您使用(yòng)的是Android模拟器,則可以通過工(gōng)具欄中的...按鈕訪問Extended Controls面闆中的電池電量

Step 3b: 使用(yòng)Kotlin添加Android平台特定的實現(xiàn)

注意: 以下(xià)步驟與步驟3a類似,隻是使用(yòng)Kotlin而不是Java。

此步驟假定您在step 1.中 使用(yòng)該-a kotlin選項創建了(le)項目

首先在Android Studio中打開(kāi)您的Flutter應用(yòng)的Android部分

啓動 Android Studio

選擇 the menu item ‘File > Open…’

定位到(dào)您 Flutter app目錄, 然後選擇裏面的 android文(wén)件夾,點擊 OK

在kotlin目錄中打開(kāi)MainActivity.kt. (注意:如果您使用(yòng)Android Studio 2.3進行編輯,請(qǐng)注意’kotlin’文(wén)件夾将顯示爲’java’。)

接下(xià)來(lái),在onCreate裏創建MethodChannel并設置一個MethodCallHandler。确保使用(yòng)與在Flutter客戶端使用(yòng)的通道(dào)名稱相同。

import android.os.Bundle

import io.flutter.app.FlutterActivity

import io.flutter.plugin.common.MethodChannel

import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity() : FlutterActivity() {

private val CHANNEL = "samples.flutter.io/battery"

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

GeneratedPluginRegistrant.registerWith(this)

MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->

// TODO

}

}

}

接下(xià)來(lái),我們添加Kotlin代碼,使用(yòng)Android電池API來(lái)獲取電池電量。此代碼與您在原生Android應用(yòng)中編寫的代碼完全相同。

首先,添加需要導入的依賴。

import android.content.Context

import android.content.ContextWrapper

import android.content.Intent

import android.content.IntentFilter

import android.os.BatteryManager

import android.os.Build.VERSION

import android.os.Build.VERSION_CODES

然後,将下(xià)面的新方法添加到(dào)activity類中的,位于onCreate 方法下(xià)方:

private fun getBatteryLevel(): Int {

val batteryLevel: Int

if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {

val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager

batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)

} else {

val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))

batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)

}

return batteryLevel

}

最後,我們完成之前添加的onMethodCall方法。我們需要處理(lǐ)平台方法名爲getBatteryLevel,所以我們在call參數中進行檢測是否爲getBatteryLevel。 這(zhè)個平台方法的實現(xiàn)隻需調用(yòng)我們在前一步中編寫的Android代碼,并使用(yòng)response參數返回成功和(hé)錯誤情況的響應。如果調用(yòng)未知(zhī)的方法,我們也(yě)會(huì)通知(zhī)返回:

MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->

if (call.method == "getBatteryLevel") {

val batteryLevel = getBatteryLevel()

if (batteryLevel != -1) {

result.success(batteryLevel)

} else {

result.error("UNAVAILABLE", "Battery level not available.", null)

}

} else {

result.notImplemented()

}

}

您現(xiàn)就可以在Android上(shàng)運行該應用(yòng)程序。如果您使用(yòng)的是Android模拟器,則可以通過工(gōng)具欄中的...按鈕訪問Extended Controls面闆中的電池電量

Step 4a: 使用(yòng)Objective-C添加iOS平台特定的實現(xiàn)

注意: 以下(xià)步驟使用(yòng)Objective-C。如果您喜歡Swift,請(qǐng)跳到(dào)步驟4b

首先打開(kāi)Xcode中Flutter應用(yòng)程序的iOS部分:

啓動 Xcode

選擇 ‘File > Open…’

定位到(dào)您 Flutter app目錄, 然後選擇裏面的 iOS文(wén)件夾,點擊 OK

确保Xcode項目的構建沒有錯誤。

選擇 Runner > Runner ,打開(kāi)`AppDelegate.m

接下(xià)來(lái),在application didFinishLaunchingWithOptions:方法内部創建一個FlutterMethodChannel,并添加一個處理(lǐ)方法。 确保與在Flutter客戶端使用(yòng)的通道(dào)名稱相同。

#import

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {

FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;

FlutterMethodChannel* batteryChannel = [FlutterMethodChannel

methodChannelWithName:@"samples.flutter.io/battery"

binaryMessenger:controller];

[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {

// TODO

}];

return [super application:application didFinishLaunchingWithOptions:launchOptions];

}

接下(xià)來(lái),我們添加ObjectiveC代碼,使用(yòng)iOS電池API來(lái)獲取電池電量。此代碼與您在本機iOS應用(yòng)程序中編寫的代碼完全相同。

在AppDelegate類中添加以下(xià)新的方法:

- (int)getBatteryLevel {

UIDevice* device = UIDevice.currentDevice;

device.batteryMonitoringEnabled = YES;

if (device.batteryState == UIDeviceBatteryStateUnknown) {

return -1;

} else {

return (int)(device.batteryLevel * 100);

}

}

最後,我們完成之前添加的setMethodCallHandler方法。我們需要處理(lǐ)的平台方法名爲getBatteryLevel,所以我們在call參數中進行檢測是否爲getBatteryLevel。 這(zhè)個平台方法的實現(xiàn)隻需調用(yòng)我們在前一步中編寫的IOS代碼,并使用(yòng)response參數返回成功和(hé)錯誤情況的響應。如果調用(yòng)未知(zhī)的方法,我們也(yě)會(huì)通知(zhī)返回:

[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {

if ([@"getBatteryLevel" isEqualToString:call.method]) {

int batteryLevel = [self getBatteryLevel];

if (batteryLevel == -1) {

result([FlutterError errorWithCode:@"UNAVAILABLE"

message:@"Battery info unavailable"

details:nil]);

} else {

result(@(batteryLevel));

}

} else {

result(FlutterMethodNotImplemented);

}

}];

您現(xiàn)在可以在iOS上(shàng)運行應用(yòng)程序。如果您使用(yòng)的是iOS模拟器,請(qǐng)注意,它不支持電池API,因此應用(yòng)程序将顯示“電池信息不可用(yòng)”。

Step 4b: 使用(yòng)Swift添加一個iOS平台的實現(xiàn)

注意: 以下(xià)步驟與步驟4a類似,隻不過是使用(yòng)Swift而不是Objective-C.

此步驟假定您在步驟1中 使用(yòng)-i swift選項創建了(le)項目。

首先打開(kāi)Xcode中Flutter應用(yòng)程序的iOS部分:

啓動 Xcode

選擇 ‘File > Open…’

定位到(dào)您 Flutter app目錄, 然後選擇裏面的 ios文(wén)件夾,點擊 OK

确保Xcode項目的構建沒有錯誤。

選擇 Runner > Runner ,然後打開(kāi)AppDelegate.swift

接下(xià)來(lái),覆蓋application方法并創建一個FlutterMethodChannel綁定通道(dào)名稱samples.flutter.io/battery:

@UIApplicationMain

@objc class AppDelegate: FlutterAppDelegate {

override func application(

_ application: UIApplication,

didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

GeneratedPluginRegistrant.register(with: self);

let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;

let batteryChannel = FlutterMethodChannel.init(name: "samples.flutter.io/battery",

binaryMessenger: controller);

batteryChannel.setMethodCallHandler({

(call: FlutterMethodCall, result: FlutterResult) -> Void in

// Handle battery messages.

});

return super.application(application, didFinishLaunchingWithOptions: launchOptions);

}

}

接下(xià)來(lái),我們添加Swift代碼,使用(yòng)iOS電池API來(lái)獲取電池電量。此代碼與您在本機iOS應用(yòng)程序中編寫的代碼完全相同。

将以下(xià)新方法添加到(dào)AppDelegate.swift底部

private func receiveBatteryLevel(result: FlutterResult) {

let device = UIDevice.current;

device.isBatteryMonitoringEnabled = true;

if (device.batteryState == UIDeviceBatteryState.unknown) {

result(FlutterError.init(code: "UNAVAILABLE",

message: "Battery info unavailable",

details: nil));

} else {

result(Int(device.batteryLevel * 100));

}

}

最後,我們完成之前添加的setMethodCallHandler方法。我們需要處理(lǐ)的平台方法名爲getBatteryLevel,所以我們在call參數中進行檢測是否爲getBatteryLevel。 這(zhè)個平台方法的實現(xiàn)隻需調用(yòng)我們在前一步中編寫的IOS代碼,并使用(yòng)response參數返回成功和(hé)錯誤情況的響應。如果調用(yòng)未知(zhī)的方法,我們也(yě)會(huì)通知(zhī)返回:

batteryChannel.setMethodCallHandler({

(call: FlutterMethodCall, result: FlutterResult) -> Void in

if ("getBatteryLevel" == call.method) {

receiveBatteryLevel(result: result);

} else {

result(FlutterMethodNotImplemented);

}

});

您現(xiàn)在可以在iOS上(shàng)運行應用(yòng)程序。如果您使用(yòng)的是iOS模拟器,請(qǐng)注意,它不支持電池API,因此應用(yòng)程序将顯示“電池信息不可用(yòng)”。

從(cóng)UI代碼中分離平台特定的代碼

如果您希望在多個Flutter應用(yòng)程序中使用(yòng)特定于平台的代碼,将代碼分離爲位于主應用(yòng)程序之外(wài)的目錄中,做一個平台插件會(huì)很(hěn)有用(yòng)。詳情請(qǐng)參閱開(kāi)發 packages 。

将平台特定的代碼作(zuò)爲一個包發布

如果您希望與Flutter生态系統中的其他(tā)開(kāi)發人員分享您的特定平台的代碼,請(qǐng)參閱發[發布 packages](/developing-packages/#publish以了(le)解詳細信息。

自(zì)定義平台通道(dào)和(hé)編解碼器

除了(le)上(shàng)面提到(dào)的MethodChannel,你(nǐ)還可以使用(yòng)BasicMessageChannel,它支持使用(yòng)自(zì)定義消息編解碼器進行基本的異步消息傳遞。 此外(wài),您可以使用(yòng)專門(mén)的BinaryCodec,StringCodec和(hé) JSONMessageCodec類,或創建自(zì)己的編解碼器。

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Flutter 文(wén)件讀寫
上(shàng)一篇:Flutter 開(kāi)發 packages