做(zuò)自(zì)由與創造的先行(xíng)者

Flutter 平台特定的代碼

Flutter開(kāi)發手冊

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

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

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

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

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

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

Platform channels architecture

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

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

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

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

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

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

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

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

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

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

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

在終端運行(xíng)中:flutter create batterylevel

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

在終端中運行(xíng): flutter create -i swift -a kotlin batterylevel

Step 2: 創建Flutter平台客戶端

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

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

通(tōng)道(dào)的客戶端和(hé)宿主通(tōng)過通(tōng)道(dào)構造函數(shù)中傳遞的通(tōng)道(dào)名稱進行(xíng)連接。單個應用中使用的所有通(tōng)道(dào)名稱必須是唯一(yī)的; 我們建議(yì)在通(tōng)道(dào)名稱前加一(yī)個唯一(yī)的“域名前綴”,例如(rú)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à)來,我們調用通(tōng)道(dào)上(shàng)的方法,指定通(tōng)過字符串标識符調用方法getBatteryLevel。 該調用可(kě)能(néng)失敗 - 例如(rú),如(rú)果平台不支持平台API(例如(rú)在模拟器中運行(xíng)時(shí)),所以我們将invokeMethod調用包裝在try-catch語句中。

我們使用返回的結果,在setState中來更新用戶界面狀态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創建包含一(yī)個小字體顯示電池狀态和(hé)一(yī)個用于刷新值的按鈕的用戶界面。

@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: 使用Java添加Android平台特定的實現

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

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

啓動 Android Studio

選擇 ‘File > Open…’

定位到您 Flutter app目錄, 然後選擇裏面的 android文件夾,點擊 OK

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

接下(xià)來,在onCreate裏創建MethodChannel并設置一(yī)個MethodCallHandler。确保使用與在Flutter客戶端使用的通(tō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à)來,我們添加Java代碼,使用Android電池API來獲取電池電量。此代碼與您在原生Android應用中編寫的代碼完全相同。

首先,添加需要(yào)導入的依賴。

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à)面的新方法添加到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方法。我們需要(yào)處理平台方法名為(wèi)getBatteryLevel,所以我們在call參數(shù)中進行(xíng)檢測是否為(wèi)getBatteryLevel。 這(zhè)個平台方法的實現隻需調用我們在前一(yī)步中編寫的Android代碼,并使用response參數(shù)返回成功和(hé)錯誤情況的響應。如(rú)果調用未知的方法,我們也會通(tōng)知返回:

@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();

}

}

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

Step 3b: 使用Kotlin添加Android平台特定的實現

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

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

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

啓動 Android Studio

選擇 the menu item ‘File > Open…’

定位到您 Flutter app目錄, 然後選擇裏面的 android文件夾,點擊 OK

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

接下(xià)來,在onCreate裏創建MethodChannel并設置一(yī)個MethodCallHandler。确保使用與在Flutter客戶端使用的通(tō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à)來,我們添加Kotlin代碼,使用Android電池API來獲取電池電量。此代碼與您在原生Android應用中編寫的代碼完全相同。

首先,添加需要(yào)導入的依賴。

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à)面的新方法添加到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方法。我們需要(yào)處理平台方法名為(wèi)getBatteryLevel,所以我們在call參數(shù)中進行(xíng)檢測是否為(wèi)getBatteryLevel。 這(zhè)個平台方法的實現隻需調用我們在前一(yī)步中編寫的Android代碼,并使用response參數(shù)返回成功和(hé)錯誤情況的響應。如(rú)果調用未知的方法,我們也會通(tōng)知返回:

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

}

}

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

Step 4a: 使用Objective-C添加iOS平台特定的實現

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

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

啓動 Xcode

選擇 ‘File > Open…’

定位到您 Flutter app目錄, 然後選擇裏面的 iOS文件夾,點擊 OK

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

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

接下(xià)來,在application didFinishLaunchingWithOptions:方法內(nèi)部創建一(yī)個FlutterMethodChannel,并添加一(yī)個處理方法。 确保與在Flutter客戶端使用的通(tō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à)來,我們添加ObjectiveC代碼,使用iOS電池API來獲取電池電量。此代碼與您在本機iOS應用程序中編寫的代碼完全相同。

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

- (int)getBatteryLevel {

UIDevice* device = UIDevice.currentDevice;

device.batteryMonitoringEnabled = YES;

if (device.batteryState == UIDeviceBatteryStateUnknown) {

return -1;

} else {

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

}

}

最後,我們完成之前添加的setMethodCallHandler方法。我們需要(yào)處理的平台方法名為(wèi)getBatteryLevel,所以我們在call參數(shù)中進行(xíng)檢測是否為(wèi)getBatteryLevel。 這(zhè)個平台方法的實現隻需調用我們在前一(yī)步中編寫的IOS代碼,并使用response參數(shù)返回成功和(hé)錯誤情況的響應。如(rú)果調用未知的方法,我們也會通(tōng)知返回:

[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);

}

}];

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

Step 4b: 使用Swift添加一(yī)個iOS平台的實現

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

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

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

啓動 Xcode

選擇 ‘File > Open…’

定位到您 Flutter app目錄, 然後選擇裏面的 ios文件夾,點擊 OK

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

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

接下(xià)來,覆蓋application方法并創建一(yī)個FlutterMethodChannel綁定通(tōng)道(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à)來,我們添加Swift代碼,使用iOS電池API來獲取電池電量。此代碼與您在本機iOS應用程序中編寫的代碼完全相同。

将以下(xià)新方法添加到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方法。我們需要(yào)處理的平台方法名為(wèi)getBatteryLevel,所以我們在call參數(shù)中進行(xíng)檢測是否為(wèi)getBatteryLevel。 這(zhè)個平台方法的實現隻需調用我們在前一(yī)步中編寫的IOS代碼,并使用response參數(shù)返回成功和(hé)錯誤情況的響應。如(rú)果調用未知的方法,我們也會通(tōng)知返回:

batteryChannel.setMethodCallHandler({

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

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

receiveBatteryLevel(result: result);

} else {

result(FlutterMethodNotImplemented);

}

});

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

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

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

将平台特定的代碼作(zuò)為(wèi)一(yī)個包發布

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

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

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

網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發
下(xià)一(yī)篇:Flutter 文件讀寫
上(shàng)一(yī)篇:Flutter 開(kāi)發 packages