Flutter 开发一个 GitHub 客户端 _ 掘金技术征文

本文详细介绍了使用Flutter开发GitHub客户端的过程,包括设计模式、架构选择、编码实践和遇到的问题。文章讨论了Flutter与React-Native的响应式编程范式,Dart语言的特性,以及数据层和UI渲染层的分离。此外,还涵盖了UI布局、Markdown和Emoji支持、Mock与缓存策略、链接拦截、全局事件总线和自定义插件的实现。最后,作者分享了项目中的坑点和总结,如代码染色的挑战以及优化计划,旨在为其他开发者提供参考。
摘要由CSDN通过智能技术生成

实现了自己的webview插件。

其它插件

我们还用到了fluttertoast 和 shared_preferences 插件,前者用于一些需要提示的场景来弹toast, 后者主要用于应用配置持久化。

设计模式及架构

遵循合适的设计模式,会让我们的代码逻辑清晰且易维护,一般来说不同端上都会有一套成熟的设计模式,如iOS上的mvc、android上的mvp、前端的mvvm等,那么我们的flutter代码中应该遵循怎样的设计模式?要回答这个问题,我们得先看一下flutter官方给出的编程范式(Flutter框架编程范式)以及google团队创造flutter时的灵感起源React-Native。

React-Native和flutter

RN最大的特点就是状态驱动的响应式编程,简而言之就是应用程序维护一套状态(state),并提供一个UI模板,而模板可以绑定状态,然后当状态发生改变时框架根据状态的变化重新构建UI界面。可见,而整个过程中用户不会直接操作UI控件树,构建过程(包括底层优化逻辑,几React中的diff算法)由框架完成。

在Flutter中,和RN非常相似,用户可以创建有_状态(stateful)和_无状态(stateless) 的widget。 然后在build方法中声明UI模板,当状态改变时,通过setState方法通知flutter, flutter会在下一个frame中调用用户提供的build方法来重建UI, 而底层的优化,如对比状态更新前后widget树的变化,只渲染变化部分的最小集,这些工作由flutter框架来完成,正如RN中的diff算法也是由框架完成一样。

所以很明显, Flutter是一个响应式框架,忘记mvxx这一套吧,如果你非要在flutter中套用mvxx这一套设计模式,很可能就会变成过度设计。

Dart语言范式

Dart语言最主要的特点就是结合了编译性语言与脚本语言之所长,特点很多,在实际动手之前,我比较关注它最受诟病的一点:在flutter中,对于复杂一点的UI,嵌套层次太深!

这一点确实无法反驳,过多的嵌套确实让代码看起来很难维护,尤其是web前端开发者,早就受够javaScript “回调地狱 ”(callback hell)之苦,没想到现在到了flutter还是逃不掉。但其实,问题并没有那么糟糕,flutter中的嵌套和javascript中的回调嵌套是不同的,javascript中的回调嵌套一般是异步任务的回调,需要在回调中处理之前回调的逻辑, 而flutter中的嵌套一般来说并不是回调,而是UI widget的声明结构,它不需要再回调中再处理逻辑,所以,flutter中也就是嵌套层次深一些,但不会发生处理逻辑混乱。目前比较好的建议就是对于复杂的ui,最好将各个部分拆分成单独函数。

架构

其实flutter本身就是响应式的框架,我们只需遵循响应式编程的规范就行,但在程序逻辑结构上,我们也要多考虑一下。由于gitme主要是通过网络从github获取数据,然后再渲染UI. 我们可以在逻辑上对业务代码简单分成两层:底层数据IO+上层UI渲染

数据层

关于数据请求的配置、逻辑等不要在UI层去控制,而由数据层自己完成。这也就是为什么我们队http库的要求中一定要包含“支持请求/响应拦截器”,因为只有支持拦截器,我们才能将io逻辑更好分离。

UI渲染层

UI层我们主要使用的事是material组件库,但我们并没有直接使用 ScaffoldAppBar 这些基本每个页面都要用的组件,而是在其上包装了一层,目的是程序风格发生变化时,我们只需要在包装组件中统一修改即可所有页面生效,而避免全局去替换(也许你会说可以设置主题,但是主题的精细粒度是不够的,有些需要自定义的点主题并不支持)。除此之外,我们也封装了一些通用的自定义组件,如支持上拉加载、下拉刷新的无限列表。

编码

在想清楚上述问题后,我们对我们APP整体也就有了一个轮廓。接下来就是去逐一解决这些技术点即可。

UI布局

布局主要涉及Flutter中widget的使用,这一步可以结合google官方 Gallery 中的示例先摸索,等自己动手写上几个页面后,布局就会轻松很多,flutter组件非常多,但常用的也很固定。flutter sdk中的注释很详细,示例都在注释里(Flutter文档就是通过注释生成的), 在IDE中可以非常方便的跳转查看源码。总之,了解Flutter widget的第一资料就是源码。

Markdown支持

dart官方有一个markdown包,它可以将markdown文本解析成html。但是我们需要的是将markdown文本直接转化成flutter widget树,所以这个包是不能直接用的,但是,如果我们要自己实现一个markdown到flutter的解析器,也并非易事。于是,我们想到了markdown包,看能否把它将markdown语法转化为html这一步替换为从markdown到flutter的widget,顺着这个思路,我们实现了最终的markdown解析器,并且工作良好。但是有一个问题就是:markdown包只支持纯粹markdown语法解析,如果在markdown文本中嵌入html代码,html代码是不支持的,所以现在我们的markdown解析器只支持markdown语法,对内嵌html代码不支持。这个我们希望markdown包作者能在后续版本中支持内嵌html语法,或者等我这边腾出手再去给它提pr。

Emoji支持

Emoji支持是在markdown解析过程中完成的,将对应的emoji标记符先转换成markdown语法,然后再解析markdown。

Mock与缓存

由于gitme中使用的网络库是dio, 而dio的开发与迭代基本与gitme是同时的,我们也花了不少的时间在dio库的迭代上。

Mock

在开发测试时,我们测试数据放在了一个git项目中,让后push到github,App访问git数据时就从github上的测试项目拉取,但是有一个问题就是每次打开页面时都要等待几秒,直到数据获取完成,这极大的影响了我们的开发效率。为了解决这个问题,我们在dio请求拦截器中做了一层mock: 如果请求的是测试项目的数据,我们直接将本地工程对应的数据返回。这样一来有两个好处:

  1. 需要添加、改动测试数据时无需push到github远程仓库,本地该了就立即生效。

  2. 节省了网络请求时间。

缓存

由于github在墙外,国内访问有时可能会在速度和稳定性上存在一些问题,为了提高用户体验,我们需要一个合理的缓存策略。一般来说,http协议有一套完整的策略,需要服务器与客户端配合(通过header来传递缓存策略信息),但是我们调用的是github的接口,所以服务器对于我们来说是不可控的,所以我们不能使用http协议本身的缓存策略,这确实比较遗憾,但是现在我们又有了一种新的思路,这还是多亏dio支持拦截器,这让我们也可以在请求前/后来定制我们的缓存策略,值得一提的是,1.0中还没有加入缓存功能,这在我们后续版本迭代时会被支持。

链接拦截

如果在markdown中点击url链接时,会进行统一的预处理,比如:检查如果是github链接的话,将其转换为App内路由,这样就可以在APP内打开,避免跳到网页中去,如果是邮箱地址,则调用系统邮箱APP打开。

全局事件总线

gitme中有些场景需要全局状态共享,这和react中的redux或vue中的vux很相似,不过gitme中需要共享的状态并不多,所以我们采用了事件总线的方式来同步状态。

插件

正如上文所说,我们需要实现一个支持一种javascript bridge协议的webview插件,这个需要会原生开发,本身难度不大,就是gitme中实现了状态栏自动变色功能,会根据背景颜色自动调整前景文字、图标颜色,这使得我们的webview插件样式比较智能,并且非常容易自定义主题。同时也实现了几个API,以供javascript调用。

我们实现的另一个插件是版本更新插件,在其中我们也集成了mta统计sdk.

修轮子

在gitme中引入了一些第三方包,而其中近乎一半的第三方包无法直接使用,对于这些包,我们的做法是fork其源码,然后修复、定制,然后在gitme中依赖我们fork的repo(flutter支持直接依赖git项目)。在开发gitme的过程中,我们深深的体会到了生态的重要性。

总结

在1.0开发完成后,首先根据之前设定的目标,check一下完成度, 然后在谈谈开发过程中躺过的坑。

目标完成度

1.0的目标基本都已完成,但仍有几个已知问题:

  1. 不支持markdown中嵌套的html代码。
  2. SVG暂不支持;原因是flutter目前不支持svg,而第三方的包质量太差,所以初始版本暂不支持。
  3. 代码染色能力不足。

对于第一个问题,上文已经谈过了,待日后优化。而代码染色问题比较棘手,这主要是因为编程语言种类繁多,而靠谱的染色方式都是需要通过将代码转化为抽象语法树(AST,Abstract Syntax Tree),然后再进行关键字、方法名、类名等提取,然后应用不同样式渲染。如果是在web端,直接引入highlight.js,但dart中目前并没有这样的库,为此我们自己实现了一个简单的分析器,我们主要测试了Dart、Javascript、Java、php四种语言的成功率,gitme 1.0.0 结果如下:

语言成功率
Dart> 95%
Javascript> 90%
Java> 90%
php50%

其它语言在1.0.0中染色成功率可能会非常低,由于良好的代码染色对gitme的用户体验非常重要,因此,我们的下个版本主要的任务就是优化代码染色,根据目前1.0.1的开发进度,我们的分析器已经足够强大,就目前的测试结果,已经支持绝大多数编程语言,并且染色成功率都在90%以上,当然,在1.0.1上线前,我们还要进行更加全面的测试,最终的结果,敬请期待!

趟过的坑

严格来说,从一开始到现在遇到的问题是挺多的,但其中大部分是由于刚接触flutter,不太熟悉,并不能说是坑,如各种widget的使用等。下面列出几个在gitme开发过程中让我们花费了较多时间的问题:

  1. 不要将build函数中传入的context保存为全局变量(可能是为了后续使用方便),build中传入的context会变,并且widget树不同部分构建时的context都不同,如果使用保存的全局context,将会出现不可预期的错误。比如无法通过context正常获取local及主题信息(偶现);

  2. 不要将需要缓存的数据保存在widget中。

由于Flutter响应式机制,每次状态变化都会重新build widget树,一般来说应该将需要缓存的数据保存在state中,由于widget和state生命周期不同,大多数情况下重新build时,state是复用的,但是发现在TabView中切换tab时,每次tab都会完全重建(包括state), 这时缓存的数据就不能放在state中,有种做法是可以将数据保存在widget中,应为widget都是你在build方法中手动创建的,只要在创建时缓存一下widget(而不是每次build都重新new一个widget),这样只要widget不重建,就可以保证保存在widget中的数据不销毁,但我告诉你,千万不要这么做,因为你缓存widget的组件本身也是可能被重建的,这样就会导致你缓存的widget还是会被重建(原来保存的数据就销毁了); 如果你非要这么做,那么久必须保证从你缓存widget的组件开始到widget树根之间的所有widget都得被缓存,否则,一旦flutter调用根widget的build方法,那么整个widget树都会被重新构建,之前缓存的数据也就自然不复存在了。正确的做法是放在全局状态管理器(如redux)或全局变量中。

  1. ListView结合RefreshIndicator 实现下拉刷新时, 列表项如果不满一屏,下拉刷新无效,此时需要将ListViewprimary 属性设置true,但设置后就不能给ListView设置controller,这是因为primary 属性设置为trueListView 会从他父辈widget中的 PrimaryScrollController 获取它的controller(每个Scaffold 都会默认设置一个PrimaryScrollController) 所以此时再设置controller时,flutter会报错,解决办法是自己手动设置一个PrimaryScrollController

  2. 当自定义导航栏(AppBar)的返回按钮时,iOS下右滑关闭手势会失效。这和iOS原生导航栏自定义返回按钮会导致右滑手势失效是一样的。

  3. Android和iOS系统支持的字体不一样,不要以为flutter会自己使用一套标准字体,flutter在绘制时也会使用系统字体,所以在Text widget指定字体时一定要看看是否两个平台都支持,gitme中在设置代码的等宽字体时发现了这个问题。

  4. 在替换图片、资源后或构建release包之前要先执行flutter clean清除缓存,否则有些时候,新的改动不会生效。

其它相关问题

除上面所述,关于Flutter, 还有一些问题可能是大家比较关心的。例如:

  1. 包大小; gitme 1.0.0 release版,Android: 11.7M, iOS AppStore上架后38M,可见android包比ios包小很多,当然,ios中各种尺寸的icon和launchImage确实会比android多占用些空间,但是这3倍的差距确实也大了一些。 笔者尚未研究flutter framework ios部分代码,至于优化空间,我想若能更好,谷歌是不会不采取行动的。

  2. 热更新; flutter release版默认是AOT,所以要实现热更新,那就只能依赖dart作为脚本语言的特性,采用JIT模式,而flutter的debug模式默认就是JIT模式,而JIT模式和AOT模式性能差距是非常大的,如果要做热更,问题瓶颈应该在性能。但是随着苹果AppStore审核策略的收紧,使用热更都会面临被拒风险,所以建议需要动态化的功能还是通过h5或rn/weex这样的框架,当然h5的风险要比rn/weex更低。

  3. 性能; Flutter AOT模式下比JIT性能好很多,如果你开发时在debug模式感觉性能不佳,可以切换到AOT模式(打Release包)试试,整体来说,flutter的性能还是符合预期的,如果Release模式下性能依然不佳,那么你就要考虑重构你的代码(或者换种实现方式)。

反馈和建议

我们之所以做gitme,最初是想做一个flutter范例,用户可以直接下载,能直观感受flutter。同时也是想做一款能够给开发者带来真正价值的APP。 我们(Flutter中文网)会继续迭代gitme,如果大家有什么好的建议或发现了bug,欢迎反馈,请在gitme issue中反馈。

下个版本计划

下个版本我们主要会在代码染色和缓存方面来优化用户体验。对于前者,上文已经仔细说过,不在赘述;对于后者,主要是因为github在墙外,在国内较慢,有时还会不稳定,所以我们考虑在APP中做一些适当的缓存策略。当然如果您有其它好的功能建议,欢迎反馈。
最后

我们欢迎您使用Gitme ,如果您觉得好,欢迎把它推荐给您的朋友、同事(菜单>分享), 也欢迎您的建议。最后再次贴出gitme官网flutterchina.club/app/gm.html
我们有一个APP体验群,您可以扫描下面二维码加入,如二维码已过期,可以添加管理员微信Demons-du(添加时请备注"gitme用户"), 他会将你拉进群。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
从 0 到 1:我的 Flutter 技术实践 | 掘金技术征文,征文活动正在进行中

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 PDF(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值