赞
踩
61、Flutter插件通信原理<一>_风雨「83」的博客-CSDN博客Flutter与原生通讯 - Flutter Plugin - 知乎前言Flutter优势主要体现在UI上--高性能且跨平台表现一致。但是针对平台(Android、IOS)的实现,如:获取电量、判断WiFi使用、调起WebView加载网页等,得调用特定平台的API包。Flutter Plugin就是为调用平台API而生。下文中所提及到的"平台"指的是Android、IOS两端。介绍Flutter Plugin包含针对Android(Java或Kotlin代码)或iOS(Objectihttps://blog.csdn.net/wywinstonwy/article/details/123925721?spm=1001.2014.3001.5502Flutter中已经具体提到了Flutter与iOS,Android的通信原理。Flutter通信是靠MethodChannel进行通信的。
假设Flutter需要一个第三方的授权登录,而第三方目前没有支持到Flutter的版本,只支持Android,iOS版本,这个时候就需要自己开发Flutter与原生的通信插件。
新建flutter_plugin工程,选择工程类型,选项语言类型,并点击finish完成工程的创建。
Android 就是我们开发安卓部分的位置
iOS 就是我们开发 iOS 的位置
lib 是与 Android 、iOS 联调的位置。也可以理解为Flutter 实现通信的位置
example 是测试的位置,当我们写完插件 可以直接运行 插件,example 可以理解为一个Flutter项目,只不过这个项目只给你写的插件服务 。
我们打开插件,找到lib ,在lib下面会有一个文件 test_flutter_plugin_demo.dart,在这个基础上我们进行扩展,更加灵活
- import 'dart:async';
-
- import 'package:flutter/services.dart';
-
- typedef void TestViewCreatedCallback(TestFlutterPluginDemo controller);
- class TestFlutterPluginDemo {
-
- late MethodChannel _channel;
- TestFlutterPluginDemo.init(int id){
- // 原生与Flutter 交互渠道
- _channel = new MethodChannel('test_flutter_plugin_demo');
- _channel.setMethodCallHandler(platformCallHandler);///设置原生参数监听
- }
-
- ///Flutter 调用原生
- ///这里我传了一个 字符串 当然也可以传Map
- Future<List<dynamic>?> changeNativeTitle(String str) async{
- return _channel.invokeListMethod('changeNativeTitle',str);
- }
-
- ///实现监听原生方法回调
- Future<dynamic> platformCallHandler(MethodCall call) async {
- switch (call.method) {
- case "clickAciton":
- print('收到原生回调 ---- $call.arguments');
- return ;
- break;
- }
- }
- }
这样就实现了 原生与Flutter之间的交互,等后续iOS,Android端代码完成就可以进行两端调试。
在example/lib下创建view.dart 文件,并在example/lib/main.dart中引入视图view.dart并调用。
view.dart全部代码
-
- import 'package:flutter/cupertino.dart';
- import 'package:test_flutter_plugin_demo/test_flutter_plugin_demo.dart';
- import 'package:flutter/services.dart';
- import 'dart:io';
-
- /// @Author wywinstonwy
- /// @Date 2022/4/15 10:58 上午
- /// @Description:
-
- ///我是使用的 StatefulWidget 使用StatelessWidget 也是一样
- class TestView extends StatefulWidget {
- ///根据自己的需求创建初始化参数
- final TestViewCreatedCallback ? onCreated; ///是上面创建的回调
- final String ? titleStr;
-
- TestView({
- required Key key,
- this.onCreated,
- this.titleStr,
- });
-
- @override
- _TestViewState createState() => _TestViewState();
- }
-
- class _TestViewState extends State<TestView> {
- @override
- Widget build(BuildContext context) {
- return Container(
- child: _loadNativeView(),
- );
- }
- ///加载原生视图
- Widget _loadNativeView(){
- ///根据不同的平台显示相应的视图
- if(Platform.isAndroid){ ///加载安卓原生视图
- return AndroidView(
- viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图
- onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调
- creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数
- 'titleStr':widget.titleStr,
- },
- /// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
- /// 如果存在 creationParams,则该值不能为null
- creationParamsCodec: const StandardMessageCodec(),
- );
- }else if(Platform.isIOS){///加载iOS原生视图
- return UiKitView(
- viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图
- onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调
- creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数
- 'titleStr':widget.titleStr,
- },
- /// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
- /// 如果存在 creationParams,则该值不能为null
- creationParamsCodec: const StandardMessageCodec(),
- );
- }else{
- return Text('这个平台老子不支持');
- }
- }
- ///这个基本上是固定写法
- Future<void> onPlatformViewCreated(id) async {
- if (widget.onCreated == null) {
- return;
- }
- widget.onCreated!(new TestFlutterPluginDemo.init(id));
- }
- }
example/lib/main.dart中调用view.dart
- import 'package:flutter/material.dart';
- import 'dart:async';
-
- import 'package:flutter/services.dart';
- import 'package:test_flutter_plugin_demo/test_flutter_plugin_demo.dart';
- import 'package:test_flutter_plugin_demo_example/view.dart';
- void main() {
- runApp(const MyApp());
- }
-
- class MyApp extends StatefulWidget {
- const MyApp({Key? key}) : super(key: key);
-
- @override
- State<MyApp> createState() => _MyAppState();
- }
-
- class _MyAppState extends State<MyApp> {
- String _platformVersion = 'Unknown';
- ///定义一个测试类的属性 用来调用原生方法 和原生交互
- var testFlutterPluginDemo;
-
- @override
- void initState() {
- super.initState();
- initPlatformState();
- }
-
- // Platform messages are asynchronous, so we initialize in an async method.
- Future<void> initPlatformState() async {
- String platformVersion ='是是是';
-
- // try {
- // platformVersion =
- // await TestFlutterPluginDemo.platformVersion ?? 'Unknown platform version';
- // } on PlatformException {
- // platformVersion = 'Failed to get platform version.';
- // }
-
-
- if (!mounted) return;
-
- setState(() {
- _platformVersion = platformVersion;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- ///初始化 测试视图的类
- TestView testView = TestView(
- onCreated: onTestViewCreated, key: ValueKey('testView'),
- titleStr: 'flutter给原生的参数',
- );
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(
- title: const Text('Plugin example app'),
- ),
- body: Column(children: [
- Container(height: 200,width: 400,child: testView,),
- FloatingActionButton(onPressed: onNativeMethon)
- ],),
- ),
- );
- }
- void onNativeMethon(){
- this.testFlutterPluginDemo.changeNativeTitle('Flutter 调用原生成功了');
- }
- void onTestViewCreated(testFlutterPluginDemo){
- this.testFlutterPluginDemo = testFlutterPluginDemo;
- }
- }
iOS 找到 ios 目录,选择Reveal in Finder,因为现在这个ios 部分还没有pod install,我们这要先进行pod install,成功后直接打开项目即可,效果如下
在这里我们找到TestFlutterPluginDemoPlugin,这个类隐藏的很深,他是Flutter 与原生交互的核心,在这了我们可以接收到Flutter的内容。
在此目录下分别创建TestFlutterPluginView和TestFlutterPluginViewFactory类
- //
- // TestFlutterPluginView.h
- // Pods
- //
- // Created by wangyun on 2022/4/15.
- //
-
- #import <Foundation/Foundation.h>
- #include <Flutter/Flutter.h>
-
- @interface TestFlutterPluginView : NSObject<FlutterPlatformView>
- - (id)initWithFrame:(CGRect)frame
- viewId:(int64_t)viewId
- args:(id)args
- messager:(NSObject<FlutterBinaryMessenger>*)messenger;
- @end
- //
- // TestFlutterPluginView.m
- // Pods
- //
- // Created by wangyun on 2022/4/15.
- //
-
- #import "TestFlutterPluginView.h"
-
- @interface TestFlutterPluginView ()
- /** channel*/
- @property (nonatomic, strong) FlutterMethodChannel *channel;
- @property (nonatomic, strong) UIButton *button;
- @property (nonatomic, strong) UITextField *textField;
- @property (nonatomic, strong) UILabel *lblText;
- @property (nonatomic, assign) NSInteger count;
- @end
-
- @implementation TestFlutterPluginView
- {
- CGRect _frame;
- int64_t _viewId;
- id _args;
-
- }
-
- - (id)initWithFrame:(CGRect)frame
- viewId:(int64_t)viewId
- args:(id)args
- messager:(NSObject<FlutterBinaryMessenger>*)messenger
- {
- if (self = [super init])
- {
- _frame = frame;
- _viewId = viewId;
- _args = args;
- NSLog(@"%@",args[@"titleStr"]);
-
-
- ///建立通信通道 用来 监听Flutter 的调用和 调用Fluttter 方法 这里的名称要和Flutter 端保持一致
- _channel = [FlutterMethodChannel methodChannelWithName:@"test_flutter_plugin_demo" binaryMessenger:messenger];
-
- __weak __typeof__(self) weakSelf = self;
-
- [_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
- [weakSelf onMethodCall:call result:result];
- }];
-
- }
- return self;
- }
-
- - (UIView *)view{
- UIView *nativeView = [[UIView alloc] initWithFrame:_frame];
- nativeView.backgroundColor = [UIColor redColor];
-
- _button = [UIButton buttonWithType:UIButtonTypeSystem];
- [_button setTitle:@"我是按钮" forState:UIControlStateNormal];
- [_button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
- [_button setBackgroundColor:[UIColor blueColor]];
- _button.frame = CGRectMake(10, 20, 100, 44);
- [nativeView addSubview:_button];
-
- self.lblText = [[UILabel alloc] initWithFrame:CGRectMake(140, 20, 200, 44)];
- self.lblText.text =_args[@"titleStr"];
- self.lblText.backgroundColor =[UIColor blueColor];
- self.lblText.textColor =[UIColor whiteColor];
- [nativeView addSubview:self.lblText];
-
- [_button addTarget:self action:@selector(flutterMethod) forControlEvents:UIControlEventTouchUpInside];
-
- _textField = [[UITextField alloc] initWithFrame:CGRectMake(100, 125, 200, 44)];
- [_textField setText: [NSString stringWithFormat:@"这个数据是原生控制 %d",_count] ];
-
-
- [nativeView addSubview:_textField];
- return nativeView;
-
- }
- #pragma mark -- Flutter 交互监听
- -(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
- //监听Fluter
- if ([[call method] isEqualToString:@"changeNativeTitle"]) {
- [_button setTitle:call.arguments forState:UIControlStateNormal];
- }
-
- }
- //调用Flutter
- - (void)flutterMethod{
- self.count = self.count+1;
- NSString *str = [NSString stringWithFormat:@"原生给flutter的参数 %ld",(long)self.count];
- [self.channel invokeMethod:@"clickAciton" arguments:str];
- [_textField setText: [NSString stringWithFormat:@"这个数据是原生控制 %ld",(long)_count] ];
- if(_count%2==0){
- self.lblText.backgroundColor =[UIColor blueColor];
- self.lblText.textColor =[UIColor whiteColor];
-
- }else{
- self.lblText.backgroundColor =[UIColor orangeColor];
- self.lblText.textColor =[UIColor blackColor];
-
- }
-
- }
- @end
- //
- // TestFlutterPluginViewFactory.h
- // Pods
- //
- // Created by wangyun on 2022/4/15.
- //
-
- #import <Foundation/Foundation.h>
- #import <Flutter/Flutter.h>
- NS_ASSUME_NONNULL_BEGIN
-
- @interface TestFlutterPluginViewFactory : NSObject<FlutterPlatformViewFactory>
- /// 重写一个构造方法 来接收 Flutter 相关蚕食
- /// @param messenger Flutter类 包含回调方法等信息
- - (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
- @end
-
- NS_ASSUME_NONNULL_END
- //
- // TestFlutterPluginViewFactory.m
- // Pods
- //
- // Created by wangyun on 2022/4/15.
- //
-
- #import "TestFlutterPluginViewFactory.h"
- #import "TestFlutterPluginView.h"
- @interface TestFlutterPluginViewFactory ()
-
- @property(nonatomic)NSObject<FlutterBinaryMessenger>* messenger;
-
- @end
-
- @implementation TestFlutterPluginViewFactory
-
- - (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
- self = [super init];
- if (self) {
- self.messenger = messenger;
- }
- return self;
- }
-
- #pragma mark -- 实现FlutterPlatformViewFactory 的代理方法
- - (NSObject<FlutterMessageCodec>*)createArgsCodec {
- return [FlutterStandardMessageCodec sharedInstance];
- }
-
- /// FlutterPlatformViewFactory 代理方法 返回过去一个类来布局 原生视图
- /// @param frame frame
- /// @param viewId view的id
- /// @param args 初始化的参数
- - (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args{
-
- TestFlutterPluginView *testFlutterPluginView = [[TestFlutterPluginView alloc] initWithFrame:frame viewId:viewId args:args messager:self.messenger];
- return testFlutterPluginView;
-
- }
-
- @end
Android 这部分和iOS 是同一个道理,没有丝毫区别,Android 我们也右键在工具中打开,然后如下图找到位置,Android 所有的代码都在这里进行
Android TestFlutterPluginView全部代码
- package com.example.test_flutter_plugin_demo;
-
- import android.content.Context;
- import android.graphics.Color;
- import android.graphics.SurfaceTexture;
- import android.provider.CalendarContract;
- import android.view.TextureView;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.FrameLayout;
- import android.widget.TextView;
- import android.widget.Toast;
-
- import androidx.annotation.NonNull;
- import io.flutter.plugin.common.BinaryMessenger;
- import io.flutter.plugin.common.MethodCall;
- import io.flutter.plugin.common.MethodChannel;
- import io.flutter.plugin.platform.PlatformView;
-
- /**
- * Created by sunyd on 1/25/22
- */
- public class TestFlutterPluginView extends TextView implements PlatformView, MethodChannel.MethodCallHandler, TextureView.SurfaceTextureListener{
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- return false;
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
- }
-
- public Context context;
- /**
- * 通道
- */
- private MethodChannel methodChannel = null;
-
- public TestFlutterPluginView(Context context, int viewId, Object args, BinaryMessenger messenger) {
- super(context);
- this.context = context;
- Toast.makeText(context, "创建关联成功", Toast.LENGTH_SHORT).show();
- setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- setBackgroundColor(Color.argb(255,79,79,79)); //0完全透明 255不透明
- //注册
- methodChannel = new MethodChannel(messenger, "test_flutter_plugin_demo");
- methodChannel.setMethodCallHandler(this);
- }
-
- @Override
- public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
- handleCall(call, result);
- }
- private void handleCall(MethodCall methodCall, MethodChannel.Result result) {
- switch (methodCall.method) {
- //开始预览
- case "changeNativeTitle":
- Toast.makeText(context, (String)methodCall.arguments, Toast.LENGTH_SHORT).show();
- break;
- default:
- }
- }
-
- @Override
- public View getView() {
- return this;
- }
-
- @Override
- public void dispose() {
-
- }
- }
Android TestFlutterPluginViewFactory全部代码
- package com.example.test_flutter_plugin_demo;
- import android.content.Context;
-
- import io.flutter.plugin.common.BinaryMessenger;
- import io.flutter.plugin.common.MessageCodec;
- import io.flutter.plugin.common.StandardMessageCodec;
- import io.flutter.plugin.platform.PlatformView;
- import io.flutter.plugin.platform.PlatformViewFactory;
-
-
- public class TestFlutterPluginViewFactory extends PlatformViewFactory {
- private BinaryMessenger messenger = null;
- public TestFlutterPluginViewFactory(BinaryMessenger messenger) {
- super(StandardMessageCodec.INSTANCE);
- this.messenger = messenger;
- }
-
- /**
- * @param createArgsCodec the codec used to decode the args parameter of {@link #create}.
- */
- public TestFlutterPluginViewFactory(MessageCodec<Object> createArgsCodec) {
- super(createArgsCodec);
- }
-
- @Override
- public PlatformView create(Context context, int viewId, Object args) {
- return new TestFlutterPluginView(context, viewId, args, this.messenger);
- }
- }
到此,插件的开发就算是完事了。实现的效果(iOS,Android)如下:
下面就是使用这个插件了,我们如何集成到 别的项目里,在这里 我们只介绍 本地 使用
其实本地使用非常简单。
1、打开我们的项目
2、打开pubspec.yaml
- dependencies:
- flutter:
- sdk: flutter
-
- test_flutter_plugin_demo:
- path: /Users/yunwang/Documents/flutterStudy/flutter_plugin_demo
flutter_plugin_demo 位插件的名称,就是我们创建插件时候的文件名称,path就是路径了,我们找到插件位置 将路径 粘贴到这里即可
3、pub get
到此就引用完成了。
4、使用我们就和example 里面一摸一样就可以了。
源码demo地址:test_flutter_plugin_demo: flutter插件开发,Android,iOS端通信。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。