Plugin Package使用, WebView交互
Flutter和原生ios/安卓交互
公開Plugin
在 Flutter 中與原生 iOS 和 Android 交互通常是通過創(chuàng)建插件(plugin)來實現(xiàn)的。這種方式允許你在 Flutter 應(yīng)用中調(diào)用原生平臺的代碼和功能。以下是創(chuàng)建和使用 Flutter 插件進行原生交互的基本步驟:
1 創(chuàng)建 Flutter 插件
使用 Flutter 命令行工具可以輕松創(chuàng)建一個插件項目:
flutter create --template=plugin my_plugin
默認語言是swift和Kotlin
如果你想指定 iOS 的語言為 Objective-C 和 Android 的語言為 Java:
flutter create --template=plugin --platforms=ios,android --ios-language=objc --android-language=java my_plugin
2 編寫原生代碼
在創(chuàng)建的插件項目中,添加 Objective-C 和 Java 的原生代碼。
3 iOS 示例(Objective-C):
在 my_plugin/ios/Classes/MyPlugin.m 文件中:
#import "MyPlugin.h"
@implementation MyPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"my_plugin"
binaryMessenger:[registrar messenger]];
MyPlugin* instance = [[MyPlugin alloc] init];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else {
result(FlutterMethodNotImplemented);
}
}
@end
4 Android 示例(Java):
在 my_plugin/android/src/main/java/com/example/my_plugin/MyPlugin.java 文件中:
package com.example.my_plugin;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
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;
/** MyPlugin */
public class MyPlugin implements FlutterPlugin, MethodCallHandler {
private MethodChannel channel;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "my_plugin");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
5 vDart 代碼交互
在 Dart 部分,你可以通過創(chuàng)建的插件與原生代碼通信。
import 'package:flutter/services.dart';
import 'package:my_plugin/my_plugin.dart';
class MyPlugin {
static const MethodChannel _channel = const MethodChannel('my_plugin');
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
6 在 Flutter 應(yīng)用中使用插件
在你的 Flutter 應(yīng)用中,添加對插件的依賴,并調(diào)用相關(guān)方法:
import 'package:my_plugin/my_plugin.dart';
// ...
Future<void> getPlatformVersion() async {
String? version = await MyPlugin.platformVersion;
print(version);
}
私有Plugin
上面的plugin一般得發(fā)布公開出去,讓別人都可以用
如果你只是自己項目專用plugin 不想公開出去
建立本地私有plugin就行
直接 ios 安卓項目里創(chuàng)建類
ios
在ios項目里新建一個JDPlutin類
.h文件
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface JDPlugin : NSObject<FlutterPlugin>
@end
NS_ASSUME_NONNULL_END
.m文件
#import "JDPlugin.h"
#import "YBKeychainUtils.h"
//app名字
#define APP_DISPLAY_NAME ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"])
//版本號1.0
#define APP_SHORT_VERSION ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"])
// build號
#define APP_BUNDLE_VERSION ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"])
@interface JDPlugin ()
@property (nonatomic, strong) FlutterMethodChannel *channel;
@end
@implementation JDPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"jd_learn_plugin" binaryMessenger:[registrar messenger]];
JDPlugin *plugin = [[JDPlugin alloc] initWithChannel:channel];;
[registrar addMethodCallDelegate:plugin channel:channel];
}
- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
NSAssert(self, @"super init cannot be nil");
self.channel = channel;
return self;
}
//收到flutter調(diào)用
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([@"getAppInfo" isEqualToString:call.method]) {
[self handgetAppInfoMethodWithParam:call.arguments result:result];
} else if ([call.method isEqualToString:@"jumpAppStore"]) {
// appStore鏈接
NSURL *url = [NSURL URLWithString:@"https://apps.apple.com/cn/app/idxxxxxx"];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
} else {
if (result) {
result(FlutterMethodNotImplemented);
}
}
}
//獲取設(shè)備信息
- (void)handgetAppInfoMethodWithParam:(id)arguments result:(FlutterResult)result {
// NSLog(@"flutter傳過來的參數(shù)---> %@", arguments);
/*
deviceId : 設(shè)備唯一id 卸載后也不變
appName : app名字
packageName: app id
appVersion 版本號
buildNumber 當前版本build號
device_model 設(shè)備機型
os_version 系統(tǒng)版本
*/
if (result) {
result( @{@"deviceId" : [YBKeychainUtils deviceId],
@"appName" : APP_DISPLAY_NAME ?: @"",
@"packageName" : [[NSBundle mainBundle] bundleIdentifier] ?: @"",
@"appVersion" : APP_SHORT_VERSION ?: @"",
@"buildNumber" : APP_BUNDLE_VERSION ?: @"",
@"device_model" : [UIDevice currentDevice].model ?: @"",
@"os_version" : [UIDevice currentDevice].systemVersion ?: @"",
});
}
}
//直接調(diào)用flutter測試
- (void)nativeCallFlutterTest {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.channel invokeMethod:@"imagePath" arguments:@{@"key1" : @"ios 調(diào)用flutter value"}];
});
}
@end
注冊私有plugin
[JDPlugin registerWithRegistrar:[self registrarForPlugin:@"JDPlugin"]];
[GeneratedPluginRegistrant registerWithRegistry:self]; 就是公開的第三方plugin,公開統(tǒng)一注冊
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import "JDPlugin.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
[JDPlugin registerWithRegistrar:[self registrarForPlugin:@"JDPlugin"]];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
安卓
1 app-src-main-java-包名 下新建JDPlugin類
package com.example.jd_flutter.jdPlugin;
import androidx.annotation.NonNull;
import java.util.HashMap;
import java.util.Map;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import com.example.jd_flutter.MainActivity;
import io.flutter.Log;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
public class JDPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler {
private final MainActivity mActivity;
public JDPlugin(MainActivity activity) {
this.mActivity = activity;
}
private MethodChannel channel;
private JDPlugin currentPlugin;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
Log.e("安卓 onAttachedToEngine", "111");
BinaryMessenger messenger = binding.getBinaryMessenger();
channel = new MethodChannel(messenger, "jd_learn_plugin");
channel.setMethodCallHandler(this);
currentPlugin = this;
this.mActivity.currentPlugin = currentPlugin;
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
channel = null;
currentPlugin = null;
this.mActivity.currentPlugin = null;
}
//flutter調(diào)用安卓
@Override
public void onMethodCall(final MethodCall call, final Result result) {
Log.e("收到原生調(diào)用11 onMethodCall", call.method);
if ("getAppInfo".equals(call.method)) {
//獲取app信息
try {
getAppInfo(call.arguments, result);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
} else {
if (result != null) {
result.notImplemented();
}
}
}
private void getAppInfo(Object args, Result result) throws PackageManager.NameNotFoundException {
// 獲取設(shè)備信息
if (result != null) {
Map<String,String> map = new HashMap();
PackageManager packageManager = this.mActivity.getApplicationContext().getPackageManager();
// 獲取當前包的信息
PackageInfo packageInfo = packageManager.getPackageInfo(this.mActivity.getApplicationContext().getPackageName(), 0);
// 獲取版本名稱和版本號
String versionName = packageInfo.versionName;
map.put("version", versionName);
map.put("buildNumber", getLongVersionCode(packageInfo));
Log.d("安卓 回調(diào)給 flutter", map.toString());
result.success(map);
}
}
String getLongVersionCode(PackageInfo info ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return Long.toString(info.getLongVersionCode());
} else {
return String.valueOf(info.versionCode);
}
}
//安卓調(diào)用flutter
private void invokeMethod(String method, Object args) {
currentPlugin.channel.invokeMethod(method, args);
}
}
注冊安卓私有plugin
package com.example.jd_flutter;
import androidx.annotation.NonNull;
import com.example.jd_flutter.jdPlugin.JDPlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
public JDPlugin currentPlugin;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
flutterEngine.getPlugins().add(new com.example.jd_flutter.jdPlugin.JDPlugin(this));
}
}
Flutter使用私有插件 獲取版本信息
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:jd_flutter/base/utils/jd_log.dart';
class JDPluginInstance {
// 推送點擊
static const String kPushClick = 'pushClick';
static const MethodChannel tdChannel = MethodChannel('jd_learn_plugin');
factory JDPluginInstance() => _instance;
static final JDPluginInstance _instance = JDPluginInstance._internal();
JDPluginInstance._internal() {
//app調(diào)用flutter方法處理
tdChannel.setMethodCallHandler((call) {
if (call.method == kPushClick) {
jdLog("native調(diào)用了flutter ${call.arguments}");
if (call.arguments is Map) {
dealPushClick(param: Map<String, dynamic>.from(call.arguments));
}
}
return Future(() {});
});
}
dealPushClick({Map<String, dynamic>? param}) {
//處理push點擊
}
Future<String> sendMsgToNative(String method,
{Map<String, dynamic>? param}) async {
jdLog("flutter 開始 native調(diào)用了flutter ");
String result = await tdChannel.invokeMethod(method, param);
jdLog("flutter 收到native的返回值 $result");
return result;
}
// 獲取設(shè)備信息
Future<Map> getAppInfo() async {
Map info = await tdChannel.invokeMethod("getAppInfo");
return info;
}
jumpToIosAppstore() {
if (!Platform.isIOS) {
return;
}
jdLog("jumpToIosAppstore");
tdChannel.invokeMethod("jumpAppStore");
}
}
Package
在 Flutter 中,"package" 指的是一個包含 Dart 代碼的模塊,它可以是純 Dart 代碼的庫,也可以是提供特定功能(如 UI 組件、工具類、網(wǎng)絡(luò)請求等)的插件。Flutter 的包和插件是它的生態(tài)系統(tǒng)的重要部分,它們允許開發(fā)者重用代碼并實現(xiàn)快速開發(fā)。
創(chuàng)建 Flutter Package
要創(chuàng)建一個新的 Flutter package,你可以使用以下命令:
flutter create --template=package my_package
這將創(chuàng)建一個包含基本目錄結(jié)構(gòu)和配置文件的新 Dart package。
包的結(jié)構(gòu)
一個典型的 Flutter package 包括以下部分:
-
lib/目錄:包含 package 的主要 Dart 代碼。 -
test/目錄:包含 package 的單元測試。 -
pubspec.yaml文件:定義了 package 的元數(shù)據(jù),包括名稱、版本、描述、依賴等。
添加依賴
在你的 Flutter 項目中,你可以通過修改 pubspec.yaml 文件來添加一個 package 作為依賴。這可以是來自 pub.dev 的公共包,也可以是本地路徑或 Git 倉庫。
dependencies:
flutter:
sdk: flutter
http: ^0.13.3 # 示例:添加 http 包
然后運行 flutter pub get 來獲取包。
使用 Package
在獲取包之后,你可以在你的項目中導(dǎo)入并使用它:
import 'package:http/http.dart' as http;
void fetchData() async {
var response = await http.get(Uri.parse('https://example.com'));
print(response.body);
}
在這個例子中,我們使用 http 包發(fā)送 HTTP 請求。
發(fā)布 Package
如果你創(chuàng)建了自己的 package,并想將其分享給社區(qū),你可以將它發(fā)布到 pub.dev。在發(fā)布之前,請確保遵循以下準則:
- 編寫有效的文檔和示例。
- 確保代碼質(zhì)量,包括格式化和單元測試。
- 遵循 Dart 的包版本管理指南。
使用 flutter pub publish 命令可以將你的 package 發(fā)布到 pub.dev。
通過創(chuàng)建和使用 Flutter packages,你可以有效地構(gòu)建和共享可重用的代碼,這是 Flutter 開發(fā)的一個重要方面。如果只是本地項目使用,則不用發(fā)布到pub.dev.
Flutter和Webview交互
在 Flutter 中,flutter_inappwebview 插件是一個非常強大的庫,用于在 Flutter 應(yīng)用中嵌入 Web 內(nèi)容,并提供了 Flutter 與 WebView 之間的交互功能。這種交互通常涉及到兩個主要方面:Flutter 向 WebView 發(fā)送數(shù)據(jù)或調(diào)用 JavaScript 函數(shù),以及 WebView 向 Flutter 發(fā)送數(shù)據(jù)或觸發(fā)事件。
- Flutter 向 WebView 發(fā)送數(shù)據(jù)或調(diào)用 JavaScript
使用 InAppWebViewController,你可以執(zhí)行 WebView 中的 JavaScript 代碼。
在這個例子中,evaluateJavascript 方法被用來在 WebView 中執(zhí)行 JavaScript 函數(shù) javascriptFunction()。
- WebView 向 Flutter 發(fā)送數(shù)據(jù)或觸發(fā)事件
你可以通過 JavaScript 向 Flutter 發(fā)送消息。首先,需要在 WebView 中定義好發(fā)送消息的 JavaScript 代碼,然后在 Flutter 中使用 InAppWebView 的 onConsoleMessage、onLoadStop 或其他相關(guān)回調(diào)來接收這些消息。
在 HTML/JavaScript 中:
// js調(diào)用flutter
function sendMessageToFlutter() { window.flutter_inappwebview.callHandler('handlerFoo', 'Hello from JS!').then(function(result) {
// 打印Flutter回調(diào)的內(nèi)容
console.log(result);
});;
}
}
//js讓flutter調(diào)用方法
function myFunction(message) {
alert("Received message: " + message);
return "Data from JavaScript";
}
Flutter 注冊js調(diào)用方法
InAppWebView(
initialUrl: "https://yourwebsite.com",
onLoadStop: (controller, url) async {
controller.addJavaScriptHandler(handlerName: 'handlerFoo', callback: (args) {
// 這里是從 JavaScript 接收到的消息
print('Received message from JavaScript: ${args[0]}');
return {'success': 1};
});
},
);
在這個例子中,當 sendMessageToFlutter() 被 JavaScript 調(diào)用時,F(xiàn)lutter 中的 addJavaScriptHandler 回調(diào)會被觸發(fā),并接收到從 JavaScript 發(fā)送的消息。
Flutter調(diào)用js方法
Future<void> callJavaScriptFunction() async {
try {
// 調(diào)用 JavaScript 函數(shù)并獲取返回值
var result = await _webViewController.evaluateJavascript(source: "myFunction('flutter string')");
print("JavaScript function returned: $result");
} catch (e) {
print("Error calling JavaScript function: $e");
}
}
封裝的flutter基礎(chǔ)框架: https:/gitee.com/kuaipai/jd_flutter,你可以參考