九月里把我的流星小屋的安卓客户端写完了,技术栈 Flutter + PHP,前后端都是我一个人维护。这篇文章记录开发过程中的几个关键问题和处理方式,也给后续打算把个人博客做成 App 的朋友们一个参考。

请输入图片描述

架构:
服务端使用了PHP来做了一个拓展接口文件,无框架无依赖。选择这个方案是因为我的这个博客本身流量不大,LAMP 环境稳定且成本几乎为零,客户端只需要处理标准 JSON,双方通过约定字段解耦。
客户端采用 Flutter开发,数据层用 http 库请求接口,状态管理用了最简单的方案,没有引入 Provider 这类库,页面间传值靠构造函数和回调。项目体量不大,架构越简单越好维护。
UTF-8 BOM 问题
联调阶段最隐蔽的一个问题。接口返回的 JSON 在浏览器里看完全正常,客户端解析却持续抛 format exception。最终通过打印响应体字节定位,报文开头携带了 EF BB BF 三个字节,即 UTF-8 BOM。原因是服务端 PHP 文件被编辑器保存为带 BOM 的编码,输出时 BOM 被一并带出,服务端无任何异常,客户端却无法通过 JSON 解析。
处理分两层,服务端文件统一转为无 BOM 编码,客户端在解析前增加防御逻辑,检测并剥离报文头部的非 JSON 字节。这类问题在跨端联调中很典型,肉眼校验文本内容没有意义,必须校验字节。
Dart 与 JS 的正则切分差异
博客文章的图片使用引用式语法,正文中的 ![1][1] 是占位符,实际地址集中定义在文末。前端需要两步处理,先切分正文提取占位符,再拼接文末的映射关系。
实现时遇到了 Dart 与 JavaScript 的行为差异。JS 的 String.split 传入带捕获组的正则时,匹配结果会保留分隔符,Dart 的同名方法则直接丢弃,切分结果缺失锚点。最终的实现改用 RegExp.allMatches 配合位置游标手工切分,行为可控。这个差异在 Dart 文档里并不显眼,属于实际踩到才知道的坑。
Kotlin 插件版本回落
集成 inappwebview(用于分享功能的长图截图)时构建失败。排查发现项目里 Kotlin 插件使用了短 ID 声明,即 id "kotlin-android",这种写法不会读取 settings.gradle 中指定的 1.8.22 版本,实际回落到了 Flutter 模板自带的 1.6.10,而 inappwebview 的元数据要求 1.8 及以上。将短 ID 全部替换为全限定名 org.jetbrains.kotlin.android 后问题消除。报错信息指向一系列无关类,定位成本较高,记录于此供搜索。
其它
分享卡片通过 inappwebview 将 H5 渲染结果截图,交给 share_plus 调起系统分享,截图需要等待图片资源加载完成并做延时兜底,否则得到的是未渲染完整的半白长图。主题系统提供十六套浅色渐变,实现上是参数化的 Gradient 配置集合,切换无成本。
客户端目前覆盖文章列表、分类、搜索、详情、点赞、评论,评论写入做了表单与 JSON 双通道以兼容不同网络环境。开源会在代码清理完成后进行,时间未定。
下载地址:https://1824396309.share.123pan.cn/123pan/qcVPjv-B7XtA
以后会开源分享给大家学习交流。