flutter 插件已经安装但是还报错_前端技术:一文带你掌握Flutter插件开发新姿势..._黄哒哒的博客-程序员资料

技术标签: flutter 插件已经安装但是还报错  

导语 |随着Flutter生态的快速发展,越来越多的Flutter插件涌现出来,那么如何开发一个Flutter插件呢?本文以具体的native_image_view插件为例,带你快速掌握Flutter插件的开发。文章作者:赵哲,腾讯游戏运营研发工程师

一、概述

随着开发技术的发展,几乎所有主流的开发语言都有自己的包管理工具。Node开发有npm、Android开发有Gradle,Flutter也有自己的Dart Packages仓库。插件的开发和复用能够提高开发效率,降低工程的耦合度,像网络请求(http)、用户授权(permission_handler)等客户端开发常用的功能模块,我们只需要引入对应插件就可以为项目快速集成相关能力,从而专注于具体业务功能的实现。

除了使用仓库中的流行组件以外,在Flutter项目开发过程中面对通用业务逻辑拆分、或者需要对原生能力封装等场景时,开发者仍然需要开发新的组件。本文以一个具体的native_image_view插件为例,将从Flutter组件的创建、开发、测试和发布等多个方面进行介绍,力图完整的展示整个Flutter组件的开发和发布流程。

二、Flutter与Native通信

在Flutter插件开发过程中,几乎都会需要进行Flutter与Native端的数据交互,因此在进行插件开发之前,我们先简单了解下Platform Channel机制。

Flutter与Native的通信是通过Platform Channel实现的,它是一种C/S模型,其中Flutter作为Client,iOS和Android平台作为Host,Flutter通过该机制向Native发送消息,Native在收到消息后调用平台自身的API进行实现,然后将处理结果再返回给Flutter页面。

Flutter中的Platform Channel机制提供了三种交互方式:

BasicMessageChannel :用于传递字符串和半结构化信息;

MethodChannel :用于传递方法调用和处理回调;

EventChannel:用于数据流的监听与发送。

这三种channel虽然用途不同,但都包含了三个重要的成员变量:

(1)String name

(2)BinaryMessager messager

作为Native与Flutter通信的载体,能够将codec转换后的二进制数据在Native与Flutter之间进行传递。每个channel在初始化时都要生成或提供对应的messager,如果channel注册了对应的handler,则messager会维护一个name与handler的映射关系。

Native平台在收到对方发来的消息后,meesager会将消息内容分发给对应的handler进行处理,在处理完成后还可以通过回调方法result将处理结果返回给Flutter。

(3)MessageCodec/MethodCodec codec

用于Native与Flutter通信过程中的编解码,在发送方能够将Flutter(或Native)的基础类型编码为二进制进行数据传输,在接收方Native(或Flutter)将二进制转换为handler能够识别的基础类型。

注:本文实现的native_image_share插件仅用到了最为常用的MethodChannel通信,Flutter通过MethodChannel将远程图片地址或本地图片文件名传递给原生侧,iOS和Android平台获取到图片后转换为二进制并通过result返回。更多关于MessageChannel和EventChannel的示例可以文末提供参考扩展阅读。

三、插件创建

Flutter组件根据是否包含原生代码可分为两种:

Flutter Package(包):仅包含dart代码,一般是对flutter特定功能的封装实现,例如用于网络请求的http包。

Flutter Plugin(插件):除了dart代码之外,还包含了Android和iOS平台的代码实现,常用于将客户端原生的能力进行封装,然后提供给flutter项目使用。例如用于判断键盘可见状态的flutter_keyboard_visibility插件,就是分别在iOS和Android端监听了键盘的打开和关闭事件,然后将对应事件通过Platform Channel传递给Flutter项目。

Flutter插件可以通过Android Studio创建(需要在Android Studio中先安装Dart和Flutter插件),或者使用命令行创建。

1. 创建Dart包

使用--template=package声明创建的是只包含dart代码的package。

lib目录用于存放package的代码实现,Flutter脚手架会自动生成一个与package同名的dart文件。

pubspec.yaml文件想必做过Flutter开发的同学都非常熟悉,我们开发package所依赖的package或者plugin都需要在该文件中声明。

2. 创建Flutter插件

使用--template=plugin声明创建的是同时包含了iOS和Android代码的plugin;

使用--org选项指定组织,一般采用反向域名表示法;

使用-i选项指定iOS平台开发语言,objc或者swift;

使用-a选项指定Android平台开发语言,java或者kotlin。

相比Package,我们可以看到Plugin多出了一些目录,android目录用于Android平台的代码实现,ios目录用于iOS平台的代码实现,example目录用于该组件的调试。

注:Flutter脚手架在创建Plugin时默认实现了一个获取系统版本号的示例,该示例的原理是分别在iOS和Android平台获取到系统版本号,然后通过MethodChannel调用返回给Flutter平台显示。

四、插件开发

Plugin和Package的开发和发布流程基本一致,相比之下Plugin还涉及到iOS和Android的开发,实现起来要更加复杂一些。

在Flutter嵌入原生项目的场景中,比较常见的一个问题是:Flutter和原生项目中都使用了同一张图片时,两侧会分别进行存储,即该图片会被存储两次。不同于Weex、Hippy等基于JS的跨平台框架是依赖于原生进行图片的获取和显示,Flutter是自行进行图片的管理并直接通过Skia引擎直接进行绘制的。

针对这一问题,本文将开发一个Flutter插件(native_image_view),把Flutter图片的下载和缓存工作交给Native实现,Flutter端则仅负责图片的绘制。此外,我们还可以定义一个特殊协议,用于处理本地图片的调用,同时解决Flutter无法复用原生项目本地图片的问题。

注:本文开发的插件仅用于介绍插件的开发和发布流程,不建议在生成环境中直接使用,关于图片二次缓存问题还可以参考扩展阅读中关于Texture(外接纹理)的文章。

1. Flutter端开发

我们首先在Flutter端声明了插件的MethodChannel,然后在initState方法中通过invokeMethod(方法名,参数)发起了对Native端的方法调用,在build方法中先显示图片的打底图,待图片数据返回后再调用setState,使用Image.memory方法将二进制数据绘制成图片显示。

native_image_view.dart:

2. Native端开发

(1)iOS开发

插件的iOS平台使用SDWebImage组件进行网络图片的下载和缓存,因此在native_image_view.podspec文件中声明依赖。

Flutter脚手架自动为我们生成了NativeImageViewPlugin.m文件和registerWithRegistrar方法,该方法是组件执行的入口,会被Flutter的插件管理器自动调用。

我们在该方法中使用与Flutter端相同的name创建MethodChannel,并创建插件对象的实例,用于处理Flutter端的方法调用。handleMethodCall方法会在MethodChannel收到Flutter端的方法调用后被触发,开发者可以通过FlutterMethodCall获取方法名和参数,通过FlutterResult返回图片内容。

NativeImageViewPlugin.m:

在处理Flutter端发起的图片调用时,首先判断Flutter请求的是本地还是网络图片,如果是本地图片则直接根据UIImage对象读取图片的二进制数据返回;如果是网络图片则先判断是否存在本地缓存,有缓存直接返回,无缓存则需要先下载图片然后再返回数据。

(2)Android开发

插件的Android平台使用Glide组件进行网络图片的下载和缓存,需要在build.gradle文件中声明依赖。

为了兼容历史版本,Android端的插件需要在onAttachedToEngine和registerWith方法中实现相同的MethodChannel注册与监听的逻辑,onMethodCall用于处理Flutter中的方法调用,也提供了与iOS平台类似的MethodCall和Result对象。

android/src/main/xxxx/NativeImageViewPlugin.java:

Android端的代码实现逻辑与iOS一致,也是先判断Flutter调用的是本地还是网络图片,对于本地图片先根据文件名获取到图片的Bitmap,然后转成byte数组返回;对于网络图片的缓存和下载基于Glide组件实现,在获取到文件缓存或下载路径后,再将文件读取为byte数组返回。

五、插件测试

Flutter脚手架在创建插件的时候自动生成了example项目,该项目通过指定插件path的方式引用了我们正在开发中的组件,让我们在发布插件之前可以进行充分的测试。

example项目除了开发调试之外,还是一种很好的插件使用示例。相比于文档,很多开发者更喜欢直接看插件example的代码实现。我们在main.dart中展示了网络图片的使用,本地图片需要原生项目中存在对应文件才可以。

main.dart:

六、插件发布

插件开发完成后就进入了发布环节,为了便于后续维护和用户反馈问题,我们将插件在github上进行维护,并在插件的pubspec.yaml文件中填写仓库地址。

在提交仓库之前,我们需要先运行dry-run命令检查组件目前是否符合发布要求。

如图所示,flutter脚手架为我们创建的pubspec.yaml文件中可能会包含author(或authors)字段,该字段已经废弃,需要删除该字段,如果要关联作者需要通过verified publisher[10]。

Flutter脚手架为我们创建的LICENSE文件是空的,需要开发者自行填写插件的开源协议。如果不填写的话dry-run不会提示,但在仓库发布那一步还是会报错。

1. 公共仓库

切记,发布在公共仓库中的插件将永久存在,flutter pub不允许开发者撤回已发布的插件,因为插件一旦发布就可能有项目依赖,而撤回组件将会破坏这种依赖关系。

直接使用publish命令,将插件发布到公共仓库:

在发布插件的过程中,可能会要求开发者登录谷歌账号进行验证,根据提示拷贝url地址在浏览器中打开,登录账户并授权即可。

在插件发布成功后,一般不能立即搜索到,需要等待pub仓库进行同步,大概15分钟左右,就可以在https://pub.dev/中搜索到刚刚发布的插件了。

公共仓库插件的使用非常简单,在插件发布以后,任何一个项目都可以通过pubspec.yaml引用该插件。

2. 私有仓库

Flutter默认发布到公共仓库,并且一旦发布就不能撤回。出于安全性考虑,在实际的业务开发中有一些组件我们暂时不想开源,而是仅限团队或公司内部使用。

这种业务场景中,一种选择是不发布组件,直接在pubspec.yaml中通过path指定本地路径、或者通过git指定仓库地址;另一种选择则是搭建内部pub仓库,将插件发布到私有仓库中。

(1)搭建私有仓库

Flutter官方提供了基于dart的pub_server组件,可以快速搭建本地运行的私有仓库服务器。pub_server没有提供类似公共仓库的web网站,但是在shelf_pubserver.dart文件中定义了仓库组件的上传、下载和删除等接口,如果有需要也可以基于该接口快速搭建web服务。

(2)配置dart运行环境

(3)安装并运行pub_server

(4)发布到私有仓库

发布到私有仓库需要在pubspec.yaml文件中新增一个publish_to字段,指定私有仓库的地址。

在发布插件时需要在发布指令中指定私有仓库的地址。

通过pub_server服务日志可以看到插件上传接口被调用,插件被成功上传到了私有仓库中。

(5)引用私有仓库组件

不同于公共仓库,在引用私有仓库中的插件时需要在pubspec.yaml文件中,通过hosted参数指定私有仓库的地址。

七、结语

本文首先介绍了Platform Channel机制,然后根据具体的业务需求详细的介绍了native_image_view插件的创建、开发和测试的过程,最后介绍了公共仓库和私有仓库的发布与使用。

Flutter组件不仅可以提供dart功能,也可以是对原生功能的封装。为了提高项目的可扩展性,减少Flutter与原生项目的耦合,将通用功能封装Flutter组件是无疑一种更好的选择。

由于时间的关系,笔者的研究尚不深入,希望能够在大家开发Flutter插件时提供一些帮助,如有疏漏和错误,欢迎在评论区留言指正交流~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_29196613/article/details/111977673

智能推荐

chmod_SearchSun的博客-程序员资料

说到chmod,就必然要提到linux对文件的分类,linux中的文件和目录其实都是当一个文件来对待的,只是有不同的标示,在一个目录下,运行 ls -l,可以看到-rwx-rw--r-- username file1.txtlinux对文件档案的权限分成三组 ,分别是用户、组、其他。一般来说,一个用户都属于某个群组,群组和用户之外的就是其他。并且权限也分三类,分别是读r、写w、执行x.可见上面的f

csapp lab2 bomb 二进制炸弹《深入理解计算机系统》_fang92的博客-程序员资料_jmpq *0x401b80(,%rax,8

bomb炸弹实验首先对bomb这个文件进行反汇编,得到一个1000+的汇编程序,看的头大。phase_1:0000000000400ef0 : 400ef0: 48 83 ec 08 sub $0x8,%rsp 400ef4: be 18 1b 40 00 mov $0x401b18,%esi 400ef9: e

区间相交问题 FZU - 1230_Piink的博客-程序员资料_区间相交问题证明

给定 x 轴上 n 个闭区间。去掉尽可能少的闭区间,使剩下的闭区间都不相交。★算法设计: 对于给定的 n 个闭区间,计算去掉的最少闭区间数。Input对于每组输入数据,输入数据的第一行是正整数 n (1<=n<=40,000),表示闭区间数。接下来的 n 行中,每行有 2 个整数,分别表示闭区间的 2 个端点。Output输出计算出的去掉的最少闭区间数。Sample Input310 2015 1020 15Sample Output2题意:去掉一些区间,使剩下的

java.util.BitSet 详细分析 学习笔记_diaoxie6274的博客-程序员资料

1,BitSet类 大小可动态改变, 取值为true或false的位集合。用于表示一组布尔标志。此类实现了一个按需增长的位向量。位 set 的每个组件都有一个 boolean 值。用非负的整数将 BitSet 的位编入索引。可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个 BitSet 修改另一个 BitSet 的...

StringUtils常用方法+StringUtils详细介绍 ._云哥中国的博客-程序员资料

StringUtils用法+StringUtils详细介绍博文来源:http://yijianfengvip.blog.163.com/blog/static/175273432201212221935832/public static void StringUtil(){    //null 和 ""操作~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

随便推点

【好程序员笔记分享】—— C语言中的结构体_SUNMEAN的博客-程序员资料_struct people* q

C语言中可以通过数组来表示每一个元素都是相同类型的数据,但当我们遇到元素之间是不同数据类型的整体刚怎么办呢?这个时候,我们就要用到结构体了。下面我们来看一个结构体的例子。代码如下:#include struct Studer{ char *name;//C语言中没有字符串类型,定义字符串用字符指针或字符数组实现 int number; char class[20

Python 学习第一天 感谢嵩天老师_bzez2003的博客-程序员资料_感谢python老师

Hello World I‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬描述这是学习每种程序设计语言的第一个实例。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬...

matlab读取视频VideoReader类_搬砖张姐的博客-程序员资料_matlab读取视频

看到以前matlab中读取视频多使用mmreader等(参考《matlab读取/播放视频的函数》),而现在matlab有一个专门的视频读取类VideoReader完成视频读取的功能。相关博文:《matlab写入/合成视频VideoWriter类 》0。 一个读取视频,显示帧,并保存每一帧 的代码fileName = 'MVI_1264_clip.avi'; obj = Vi

python里使用reduce()函数_caimouse的博客-程序员资料

reduce()函数在库functools里,如果要使用它,要从这个库里导入。reduce函数与map函数有不一样地方,map操作是并行操作,reduce函数是把多个参数合并的操作,也就是从多个条件简化的结果,在计算机的算法里,大多数情况下,就是为了简单化。比如识别图像是否是一只猫,那么就是从众多的像素里提炼出来一个判断:是或否。可能是几百万个像素,就只出来一个结果。在GOOGLE大规模集群里,就

react antd常用插件二次封装_weixin_30278237的博客-程序员资料

import React, { Component } from 'react';import './index.less';import moment from 'moment';import {checkNull} from '../tools.js'import {Select,Input,message} from 'antd'const TextArea = In...

promise_trouble-Boy的博客-程序员资料

原文链接http://www.hangge.com/blog/cache/detail_1639.htmlJS - Promise使用详解1(基本概念、使用优点)一、promises相关概念promises 的概念是由 CommonJS 小组的成员在 Promises/A 规范中提出来的。1,then()方法介绍根据 Promise/A 规范,promise 是一个对象,只需要 th...

推荐文章

热门文章

相关标签