前言导读
之前有同学问我flutter插件如何开发, 今天就分享一起flutter插件的开发。带着大家从零开始开发属于自己的插件
效果图
具体实现
-
创建插件工程
flutter create -t plugin --platforms ohos,android,ios <plugin_name>
-
1 我们的lib目录下面编写我们的插件和原生交互的dart代码
-
2 example 下面是我们调试插件的代码
-
3 iOS 是我们的插件原生部分代码
-
我们选直接运行下我们的项目 让 Xcode拉一下依赖
-
我们打开example 项目下面的iOS工程 (谨记不是根目录下面的 iOS工程)
手动拉依赖。
进到我们的example 目录下面的ios目录下面 执行pod install
$ cd ios
$ pod instal
使用 xcode 打开 example / ios 目录
这个demo我们一共编写了 获取iOS系统版本号和toast的案例
-
获取iOS系统版本号
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
-
toast
case "toast":
let args = call.arguments as? [String: Any] ?? [:] // 获取参数
let data = args["param"] as? String ?? "default" // 提取数据
ToastManager.show(message:data)
这里需要注意我们flutter 端 是通过map传递参数给我们鸿蒙next端的
@override
toast(String str) async {
Map<String,dynamic>user={
'param':str
};
await methodChannel.invokeMethod<String>('toast',user);
}
-
完整插件原生iOS代码
import Flutter
import UIKit
public class DemopluginPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "demoplugin", binaryMessenger: registrar.messenger())
let instance = DemopluginPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
case "toast":
let args = call.arguments as? [String: Any] ?? [:] // 获取参数
let data = args["param"] as? String ?? "default" // 提取数据
ToastManager.show(message:data)
default:
result(FlutterMethodNotImplemented)
}
}
}
-
iOS toast工具类 ToastView
class ToastView: UIView {
private let label: UILabel = {
let label = UILabel()
label.textColor = .white
label.textAlignment = .center
label.numberOfLines = 0
label.font = .systemFont(ofSize: 14)
return label
}()
init(message: String) {
super.init(frame: .zero)
backgroundColor = UIColor(white: 0, alpha: 0.7)
layer.cornerRadius = 8
addSubview(label)
label.text = message
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
label.topAnchor.constraint(equalTo: topAnchor, constant: 8),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
-
toast 显示工具类 ToastManager
//
// ToastManager.swift
// Pods
//
// Created by xuqing on 2025/7/15.
//
class ToastManager {
private static var toastWindow: UIWindow?
static func show(message: String, duration: TimeInterval = 2.0) {
DispatchQueue.main.async {
let screenSize = UIScreen.main.bounds.size
let toastView = ToastView(message: message)
toastView.frame = CGRect(x: 0, y: screenSize.height-100,
width: screenSize.width, height: 60)
toastWindow = UIWindow(frame: UIScreen.main.bounds)
toastWindow?.windowLevel = .alert + 1
toastWindow?.backgroundColor = .clear
toastWindow?.addSubview(toastView)
toastWindow?.makeKeyAndVisible()
UIView.animate(withDuration: 0.3, delay: duration, options: []) {
toastView.alpha = 0
} completion: { _ in
toastWindow?.isHidden = true
toastWindow = nil
}
}
}
}
-
flutter端插件代码实现
我们先再插件父类抽象类里面 定义我们跟原生端交互的方法
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'demoplugin_method_channel.dart';
abstract class DemopluginPlatform extends PlatformInterface {
/// Constructs a DemopluginPlatform.
DemopluginPlatform() : super(token: _token);
static final Object _token = Object();
static DemopluginPlatform _instance = MethodChannelDemoplugin();
/// The default instance of [DemopluginPlatform] to use.
///
/// Defaults to [MethodChannelDemoplugin].
static DemopluginPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [DemopluginPlatform] when
/// they register themselves.
static set instance(DemopluginPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
@override
toast(String str) async {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}
-
然后再我们的 MethodChannelComeplugin 类里面实现这些方法
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'demoplugin_platform_interface.dart';
/// An implementation of [DemopluginPlatform] that uses method channels.
class MethodChannelDemoplugin extends DemopluginPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('demoplugin');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
@override
toast(String str) async {
Map<String,dynamic>user={
'param':str
};
await methodChannel.invokeMethod<String>('toast',user);
}
}
-
我们通过 MethodChannel 来注册我们的交互的通信的协议demoplugin 这个要跟我的原生端保持一致
-
flutter 端
@visibleForTesting
final methodChannel = const MethodChannel('demoplugin');
-
iOS原生端
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "demoplugin", binaryMessenger: registrar.messenger())
let instance = DemopluginPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
-
然后我们定义 Demoplugin 定义对应的对外方法 给我们的 example 调试工程调用
import 'demoplugin_platform_interface.dart';
class Demoplugin {
Future<String?> getPlatformVersion() {
return DemopluginPlatform.instance.getPlatformVersion();
}
@override
toast(String str) async {
return DemopluginPlatform.instance.toast(str);
}
}
我们在Demoplugin 里面里面返回我们的 DemopluginPlatform 里面的结果
然后我们在 example里面去调用
-
获取iOS系统版本号调用
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _demopluginPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
-
toast调用
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
children: [
Text('Running on: $_platformVersion\n'),
ElevatedButton(onPressed: ()=>{
_demopluginPlugin.toast("测试数据")
}, child: Text("点击toast"))
],
)
),
),
);
}
最后总结
到此我们的flutter 插件开发我们就讲完了,那么如何打包插件发布,我们在后面我们补上去,因为篇幅有限我这边就不展开讲,如果同学有兴趣可以自己研究 也可以继续等待老师后续的更新,今天的文章就讲到这里,, 今天的文章就讲到这里有兴趣的 关注我B站教程 谢谢
-
如果想看Flutter插件开发化android交互在这里
-
如果想看Flutter插件开发化 鸿蒙交互看这里