Plugin Package使用, WebView交互
Flutter和原生ios/安卓交互
公开Plugin
在 Flutter 中与原生 iOS 和 android 交互通常是通过创建插件(plugin)来实现的。这种方式允许你在 Flutter 应用中调用原生平台的代码和功能。以下是创建和使用 Flutter 插件进行原生交互的基本步骤:
1 创建 Flutter 插件
使用 Flutter 命令行工具可以轻松创建一个插件项目:
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 编写原生代码
在创建的插件项目中,添加 Objective-C 和 Java 的原生代码。
3 iOS 示例(Objective-C):
在 my_plugin/ios/Classes/MyPlugin.m
文件中:
#import "MyPlugin.h"
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);
}
}
4 Android 示例(Java):
在 my_plugin/android/src/main/java/***/example/my_plugin/MyPlugin.java
文件中:
package ***.example.my_plugin;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.***mon.MethodCall;
import io.flutter.plugin.***mon.MethodChannel;
import io.flutter.plugin.***mon.MethodChannel.MethodCallHandler;
import io.flutter.plugin.***mon.MethodChannel.Result;
/** MyPlugin */
public class MyPlugin implements FlutterPlugin, MethodCallHandler {
private MethodChannel channel;
public void onAttachedToEngine( FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "my_plugin");
channel.setMethodCallHandler(this);
}
public void onMethodCall( MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.su***ess("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
public void onDetachedFromEngine( FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
5 vDart 代码交互
在 Dart 部分,你可以通过创建的插件与原生代码通信。
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 应用中使用插件
在你的 Flutter 应用中,添加对插件的依赖,并调用相关方法:
import 'package:my_plugin/my_plugin.dart';
// ...
Future<void> getPlatformVersion() async {
String? version = await MyPlugin.platformVersion;
print(version);
}
私有Plugin
上面的plugin一般得发布公开出去,让别人都可以用
如果你只是自己项目专用plugin 不想公开出去
建立本地私有plugin就行
直接 ios 安卓项目里创建类
ios
在ios项目里新建一个JDPlutin类
.h文件
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
JDPlugin : NSObject<FlutterPlugin>
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"])
JDPlugin ()
(nonatomic, strong) FlutterMethodChannel *channel;
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调用
- (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.***/***/app/idxxxxxx"];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} ***pletionHandler:nil];
}
} else {
if (result) {
result(FlutterMethodNotImplemented);
}
}
}
//获取设备信息
- (void)handgetAppInfoMethodWithParam:(id)arguments result:(FlutterResult)result {
// NSLog(@"flutter传过来的参数---> %@", arguments);
/*
deviceId : 设备唯一id 卸载后也不变
appName : app名字
packageName: app id
appVersion 版本号
buildNumber 当前版本build号
device_model 设备机型
os_version 系统版本
*/
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 ?: @"",
});
}
}
//直接调用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 调用flutter value"}];
});
}
注册私有plugin
[JDPlugin registerWithRegistrar:[self registrarForPlugin:@"JDPlugin"]];
[GeneratedPluginRegistrant registerWithRegistry:self]; 就是公开的第三方plugin,公开统一注册
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import "JDPlugin.h"
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];
}
安卓
1 app-src-main-java-包名 下新建JDPlugin类
package ***.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 ***.example.jd_flutter.MainActivity;
import io.flutter.Log;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.***mon.BinaryMessenger;
import io.flutter.plugin.***mon.MethodCall;
import io.flutter.plugin.***mon.MethodChannel;
import io.flutter.plugin.***mon.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;
public void onAttachedToEngine( 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;
}
public void onDetachedFromEngine( FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
channel = null;
currentPlugin = null;
this.mActivity.currentPlugin = null;
}
//flutter调用安卓
public void onMethodCall(final MethodCall call, final Result result) {
Log.e("收到原生调用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 {
// 获取设备信息
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("安卓 回调给 flutter", map.toString());
result.su***ess(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);
}
}
//安卓调用flutter
private void invokeMethod(String method, Object args) {
currentPlugin.channel.invokeMethod(method, args);
}
}
注册安卓私有plugin
package ***.example.jd_flutter;
import androidx.annotation.NonNull;
import ***.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;
public void configureFlutterEngine( FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
flutterEngine.getPlugins().add(new ***.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调用flutter方法处理
tdChannel.setMethodCallHandler((call) {
if (call.method == kPushClick) {
jdLog("native调用了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调用了flutter ");
String result = await tdChannel.invokeMethod(method, param);
jdLog("flutter 收到native的返回值 $result");
return result;
}
// 获取设备信息
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 组件、工具类、网络请求等)的插件。Flutter 的包和插件是它的生态系统的重要部分,它们允许开发者重用代码并实现快速开发。
创建 Flutter Package
要创建一个新的 Flutter package,你可以使用以下命令:
flutter create --template=package my_package
这将创建一个包含基本目录结构和配置文件的新 Dart package。
包的结构
一个典型的 Flutter package 包括以下部分:
-
lib/
目录:包含 package 的主要 Dart 代码。 -
test/
目录:包含 package 的单元测试。 -
pubspec.yaml
文件:定义了 package 的元数据,包括名称、版本、描述、依赖等。
添加依赖
在你的 Flutter 项目中,你可以通过修改 pubspec.yaml
文件来添加一个 package 作为依赖。这可以是来自 pub.dev的公共包,也可以是本地路径或 Git 仓库。
dependencies:
flutter:
sdk: flutter
http: ^0.13.3 # 示例:添加 http 包
然后运行 flutter pub get
来获取包。
使用 Package
在获取包之后,你可以在你的项目中导入并使用它:
import 'package:http/http.dart' as http;
void fetchData() async {
var response = await http.get(Uri.parse('https://example.***'));
print(response.body);
}
在这个例子中,我们使用 http
包发送 HTTP 请求。
发布 Package
如果你创建了自己的 package,并想将其分享给社区,你可以将它发布到 pub.dev。在发布之前,请确保遵循以下准则:
- 编写有效的文档和示例。
- 确保代码质量,包括格式化和单元测试。
- 遵循 Dart 的包版本管理指南。
使用 flutter pub publish
命令可以将你的 package 发布到 pub.dev。
通过创建和使用 Flutter packages,你可以有效地构建和共享可重用的代码,这是 Flutter 开发的一个重要方面。如果只是本地项目使用,则不用发布到pub.dev.
Flutter和Webview交互
在 Flutter 中,flutter_inappwebview
插件是一个非常强大的库,用于在 Flutter 应用中嵌入 Web 内容,并提供了 Flutter 与 WebView 之间的交互功能。这种交互通常涉及到两个主要方面:Flutter 向 WebView 发送数据或调用 JavaScript 函数,以及 WebView 向 Flutter 发送数据或触发事件。
- Flutter 向 WebView 发送数据或调用 JavaScript
使用 InAppWebViewController
,你可以执行 WebView 中的 JavaScript 代码。
在这个例子中,evaluateJavascript
方法被用来在 WebView 中执行 JavaScript 函数 javascriptFunction()
。
- WebView 向 Flutter 发送数据或触发事件
你可以通过 JavaScript 向 Flutter 发送消息。首先,需要在 WebView 中定义好发送消息的 JavaScript 代码,然后在 Flutter 中使用 InAppWebView
的 onConsoleMessage
、onLoadStop
或其他相关回调来接收这些消息。
在 HTML/JavaScript 中:
// js调用flutter
function sendMessageToFlutter() { window.flutter_inappwebview.callHandler('handlerFoo', 'Hello from JS!').then(function(result) {
// 打印Flutter回调的内容
console.log(result);
});;
}
}
//js让flutter调用方法
function myFunction(message) {
alert("Received message: " + message);
return "Data from JavaScript";
}
Flutter 注册js调用方法
InAppWebView(
initialUrl: "https://yourwebsite.***",
onLoadStop: (controller, url) async {
controller.addJavaScriptHandler(handlerName: 'handlerFoo', callback: (args) {
// 这里是从 JavaScript 接收到的消息
print('Received message from JavaScript: ${args[0]}');
return {'su***ess': 1};
});
},
);
在这个例子中,当 sendMessageToFlutter()
被 JavaScript 调用时,Flutter 中的 addJavaScriptHandler
回调会被触发,并接收到从 JavaScript 发送的消息。
Flutter调用js方法
Future<void> callJavaScriptFunction() async {
try {
// 调用 JavaScript 函数并获取返回值
var result = await _webViewController.evaluateJavascript(source: "myFunction('flutter string')");
print("JavaScript function returned: $result");
} catch (e) {
print("Error calling JavaScript function: $e");
}
}
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题