flutter中pdf的生成、预览、分享实现
公司最近的app新版本需求里有个pdf报告的部分,为了减少工作量,决定使用支持跨平台的flutter来开发这个模块
这里记录一下开发各个阶段遇到的问题,以及自己的各种解决方案,及每个方案的优劣和我自己的心路历程
作为一个前端,在此之前虽然听说过flutter,但还从未学习过,从零开始用这样的新兴技术上操刀,其实真个心路历程都是痛苦的,至于代码方面,也是由开始的一坨shit,慢慢变成了半坨shit,虽然有进步,但是这些代码各位看官也就看看就行了
最终成品是用的flutter_html_to_pdf插件生成pdf,pdfx预览pdf,原生调用分享,整体来说代码相对比较干净
1|0pdf的生成
1|1方案历程
1|2暴力截图
- 暴力截图的本质是对页面的根组件进行定位,然后将该组件整个按比例生成一张图片
- 所以截图的前提是有页面,所以这个方案其实对应了预览部分的第一个方案,需要先用flutter语法搭建pdf页面
- flutter搭建页面用的是dart语法,总体来说不算难,但是现学现卖,写出来的代码就是shit,这里就不贴了,但是总体上搭出来的pdf页面还是比较完美的,毕竟dart也支持flex布局,有这个东西,对于一个前端来说,写个pdf挺简单的
- 有了页面,那么接下来就是截图了
- 首先需要定位目标跟组件,也就是不带页面appBar部分的纯pdf部分。flutter中有个叫globalKey的对象,是一个具有全局唯一性的对象,用它可以绑定页面中任意组件,并在页面渲染之后的任意时刻获取该绑定组件的状态、结构
- 用法:
先在HomePageState里声明一个全局key:GlobalKey mykey = new GlobalKey();
然后再pdf组件的根Container里添加Container(key: mykey, child: PdfContainer())
最后再需要用到该组件时,通过mykey.currentContext
即可获取当前组件,重点注意,要获取到mykey绑定的组件,前提是已经成功绑定了,也就是说,页面mounted之后,在这之前获取到的是空,所以如果报错mykey.currentContext为null,请排查并判断自己使用mykey.currentContext的时候,组件是否已经绑定 - 定位到了组件,然后就是生成图片了,这里主要用了
Image.memory
对二进制数据进行图片化
- 但是这里的生成函数是一个Future函数,这是为了配合loading组件的使用,后面再细说
- 图片生成好了,就用pdf插件放一张图就好了,比较简单,就不放代码了
1|3pdf插件语法搭建
- 明明是官方插件,也不知道是哪个大哥写的,其实和dart语法真的很像,但是却又因为不一样,需要和页面分开重新搭建一个pdf结构,虽然整体结构和dart搭建的页面结构基本一模一样,只是组件名不一样,然后会有一些属性不兼容,所以搭建挺快的,但是还是不妨碍我说它sb,就非常后悔选用这个。
- 虽然sb,但是还是完整的记录下自己的踩坑历程
- pdf插件生成pdf文件的过程就是搭建的过程,先声明一个
pdf = pw.Document()
,然后pdf.addPage()
就可以开始按照dart搭页面的方式搭pdf了,就不贴代码了,直接复制dart搭建的页面的代码,然后改成pw的widget就行了,相对简单 - 但是pdf插件有一个很大的坑
- 这个插件不能识别除了英文以外的任何语言,需要导入字体库,这可以说非常的机车
- 而我们的app要兼容7种语言,还有regular和bold两种字重,而且还需要考虑使用的字体库的版权问题,是否能免费商用,然后找到了合适的字体库,发现总共有20+M,直接导致了APP打包以后也变大了20+M,这是产品那边基本不能接受的,所以第一期先这么做,毕竟不熟,但是这个pdf插件的方案肯定是需要替换的
- 这也顺道引出了flutter中多语言配置的使用,链接传送门
1|4flutter_html_to_pdf插件
- 这也是项目最终使用的生成插件,用html搭建pdf页面,非常契合我web前端的身份,但是也意味着需要重新搭pdf页面了
- 这个插件也有几个比较坑的地方
- 第一个坑:有数据插入的页面不能直接读html文件,就算在html文件中写了插值,也不能实现,基本写法如下
- 可见,这里的html标签全是写成了字符串,然后用${}进行插值,不过这个插件厉害的地方在于,它能够识别js语法,如上,我这里是插入了一个
scriptHtml
的,所以我们可以在js里画图或者操作dom之类的 - 第二个坑:html里引用图片不能直接通过项目路径来访问,而需要通过打包后的app路径来访问
- 先来看看插件官方文档的说法
- 可见有三种引用图片的方式,第三种用url链接的方式访问很直观,也简单,要是公司有自己的cdn服务器可以直接用这个,但是公司没有,也不好放到免费的cdn上,所以看看前两种吧。其实前两种都涉及一个问题,我们需要先确认,这里引用的路径到底是项目静态资源路径,还是app路径,尝试一下项目资源路径就知道,是引用不了的,所以我们尝试一下app路径能不能引用到,但是app的可供访问的的storage里没有我们的图片,我们还得先把图片放进去
- 然后appdir里有我们需要用的图片了,就可以通过src="file:///${appDir.path}/pdfLogo.png"的写法引入图片了,注意前面的file:///
- 至于appdir:
appDir = await getApplicationDocumentsDirectory();
存个全局的就行了 - 第三个也不能算坑,就是提高效率的东西,dart不能像我们用vue这些框架一样用if和for标签进行渲染,但是我们可以用“组件化”的方式,用dart也实现这个写法,比如:
2|0pdf的预览
2|1方案历程
2|2flutter_full_pdf_viewer插件
- 首先预览pdf文件都需要注意一个问题,打开文件之前pdf必须生成完了,所以这就需要一个loading组件来给个缓冲,前文也提到了生成pdf我们用的是Future函数,就是在这里用到
- 目前看下来好像这个插件没啥问题啊,为啥被放弃了呢?大概就是不符合我们的使用场景吧,我们的app的其他部分都还是用的原生的android和ios搭建的,只有pdf这个页面用了flutter,所以现在的场景是,从原生点击入口进入到我的flutter页面,pdf的预览就是首页,而这个插件预览pdf,他是需要一个跳转操作的,即在当前页面通过一个入口,触发后通过navigator跳转到预览pdf的页面,这样就会多一个页面跳转的动画,也会导致返回上一页即返回原生页面出bug,所以被放弃了。
- 如果你的使用场景是通过如果跳转预览文件,可以使用这个插件
2|3flutter搭建页面
- 纯dart语法搭建一个页面,这个做法比较粗暴,也简单,就是工作量较大,因为我们已经使用了html搭建pdf结构,所以还需要重新搭建一套dart的pdf结构
- 这个做法的优点在于不用loading,进flutter页面直接预览
- 但是这个做法一个非常大的弊端在于,无法缩放、拖拽pdf,要做手势判断其实也行,但是其工作量非常巨大,因为会导致组件布局变形,这是得不偿失的,而由于不能缩放,在产品那边不过关,被卡掉了
2|4预览图片
- dart搭建的pdf页面都搭好了,不想做无用功,就结合前面的对跟组件截图生成图片的方式,为啥不把这个页面改成一张图呢,如果是图不就好做缩放了吗?说干就干
- 截图方式
- 然后将页面替换成生成的图片简单,问题是图片的缩放、拖拽的实现,传送门
- 这种方式的预览,到测试那儿被说不够流畅,用户体验不太好,还是需要改动
2|5pdfx插件
- 最后用了pdfx插件,这个插件就没有flutter_full_pdf_viewer插件的跳转问题,直接是一个
PdfController
来展示预览的文件 - 在生成之前,除了loading,我们还需要一个空Container来替换PdfController
- 这里有个WillPopScope组件,是flutter中用来触发手机的返回事件的,如右滑手机屏幕,还有手机的返回键,这些触发的返回事件,传送门
- pdfx插件整体来说挺不错的,也没遇到啥坑
3|0pdf的分享
- 这个比较简单,从始至终实现了就没变过,调原生分享通道,flutter调用原生通道有两种,一种是
EventChannel
,一种是MethodChannel
__EOF__

本文作者:Mizuki
本文链接:https://www.cnblogs.com/mizuki-vone/p/16373416.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/mizuki-vone/p/16373416.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库