flutter Plugin Package使用, WebView交互

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ā)布之前,請確保遵循以下準則:

使用 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ā)事件。

  1. Flutter 向 WebView 發(fā)送數(shù)據(jù)或調(diào)用 JavaScript

使用 InAppWebViewController,你可以執(zhí)行 WebView 中的 JavaScript 代碼。

在這個例子中,evaluateJavascript 方法被用來在 WebView 中執(zhí)行 JavaScript 函數(shù) javascriptFunction()。

  1. WebView 向 Flutter 發(fā)送數(shù)據(jù)或觸發(fā)事件

你可以通過 JavaScript 向 Flutter 發(fā)送消息。首先,需要在 WebView 中定義好發(fā)送消息的 JavaScript 代碼,然后在 Flutter 中使用 InAppWebViewonConsoleMessage、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,你可以參考

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

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

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