1. 项目背景与核心需求
作为一名长期追番的动画爱好者,我经常遇到这样的困扰:不同平台的番剧更新速度不一,有些冷门番剧甚至找不到稳定的观看源。市面上的聚合类应用要么功能臃肿,要么缺乏自定义能力。这促使我开发了一个基于Flutter的跨平台解决方案,它能够:
- 通过自定义采集规则抓取多个片源站的番剧数据
- 自动聚合更新信息并去除重复资源
- 提供统一的播放界面和观看记录同步
- 支持Android/iOS双平台运行
这个项目的独特之处在于其规则引擎设计,用户可以通过简单的JSON配置定义采集规则,无需重新编译应用即可扩展新的片源站。下面我将从技术选型到具体实现,完整分享这个项目的开发历程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构与选型考量
2.1 为什么选择Flutter
跨平台能力是本项目的核心需求之一。经过对比React Native和原生开发方案后,Flutter在以下方面展现出明显优势:
- 渲染性能:自绘引擎避免了JavaScript桥接的瓶颈,实测在低端Android设备上也能保持60fps的列表滚动
- 开发效率:热重载功能使UI调试效率提升3倍以上
- 插件生态:现有插件已覆盖视频播放、网络请求等核心功能需求
提示:Flutter 3.44版本对自定义绘制做了显著优化,特别适合需要复杂UI交互的视频类应用
2.2 核心模块分解
系统架构分为四个主要层次:
code复制┌─────────────────┐
│ UI Layer │ # Flutter实现的跨平台界面
├─────────────────┤
│ Service Layer │ # 数据采集/解析/缓存服务
├─────────────────┤
│ Rule Engine │ # 动态加载的自定义规则处理器
├─────────────────┤
│ Native Bridge │ # 平台相关的功能适配层
└─────────────────┘
3. 规则引擎实现细节
3.1 规则定义规范
采集规则采用JSON格式,包含以下关键字段:
json复制{
"site": "example",
"entryUrl": "https://example.com/latest",
"itemSelector": ".video-item",
"fields": {
"title": {"selector": "h3", "attr": "text"},
"cover": {"selector": "img.thumb", "attr": "src"},
"detailUrl": {"selector": "a.link", "attr": "href"}
}
}
字段说明:
itemSelector使用CSS选择器语法定位番剧条目attr支持text/html/attr三种提取方式- 支持XPath作为备选选择器方案
3.2 动态加载机制
通过Dart的isolate实现规则隔离执行,关键代码:
dart复制Future<dynamic> executeRule(String ruleJson) async {
final receivePort = ReceivePort();
await Isolate.spawn(_ruleIsolate, receivePort.sendPort);
return receivePort.first;
}
void _ruleIsolate(SendPort port) {
// 规则解析和执行逻辑
}
这种设计带来三个好处:
- 避免错误规则导致主线程崩溃
- 支持并行执行多个采集任务
- 内存空间隔离更安全
4. 播放器集成实践
4.1 播放器选型对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| flutter_vlc_player | 格式支持广 | 包体积大(+15MB) | 需要蓝光画质 |
| video_player | 官方维护 | 依赖平台解码器 | 常规MP4资源 |
| chewie | 交互体验好 | 二次开发成本高 | 需要定制UI |
最终选择video_player+自定义控制层的方案,在兼容性和体验间取得平衡。
4.2 播放记录同步设计
采用分层缓存策略:
- 内存缓存最近5条的精确进度(秒级)
- SQLite存储全量记录(包含最后观看时间)
- 可选同步到Firebase(需登录)
关键实现:
dart复制class PlaybackTracker {
final Map<String, PlaybackRecord> _memoryCache = {};
void updateRecord(String episodeId, Duration position) {
_memoryCache[episodeId] = PlaybackRecord(
updatedAt: DateTime.now(),
position: position
);
// 异步持久化
_scheduleDiskWrite();
}
}
5. 性能优化关键点
5.1 列表渲染优化
番剧列表采用ListView.builder+AutomaticKeepAlive组合:
dart复制ListView.builder(
itemCount: items.length,
itemBuilder: (ctx, index) => KeepAliveWrapper(
child: AnimeItem(item: items[index]),
),
)
配合以下技巧:
- 预加载封面图(使用cached_network_image)
- 分页加载阈值设为当前长度的70%
- 对复杂item启用
repaintBoundary
5.2 内存管理实践
通过DevTools发现的典型问题及解决方案:
| 问题现象 | 排查工具 | 解决方案 |
|---|---|---|
| 图片内存泄漏 | Memory Tab | 实现图片卸载监听 |
| 规则isolate未释放 | Heap Snapshot | 增加isolate.dispose() |
| 播放器实例堆积 | Object Graph | 引入对象池模式 |
6. 实际效果与扩展思考
经过三个月的开发和迭代,目前实现了:
- 支持12种常见片源站的自定义采集
- Android端平均启动时间<800ms
- 播放器首帧加载时间优化至1.2s内
几个值得分享的踩坑经验:
- 安卓WebView的UserAgent需要显式设置,否则某些站点会返回移动端页面
- iOS平台需要额外配置ATS例外才能访问非HTTPS资源
- Flutter的Platform Channel在频繁调用时会出现性能瓶颈
未来可能的改进方向:
- 增加规则市场功能,让用户可以分享采集配置
- 集成弹幕协议支持
- 开发Windows/macOS桌面端版本
这个项目最让我惊喜的是Flutter在复杂业务场景下的表现力。通过合理的设计,用一套代码同时实现了Android和iOS端90%以上的功能共享,这在传统原生开发中是不可想象的。对于想要入门跨平台开发的同学,我的建议是从实际需求出发,先构建最小可行产品,再逐步扩展功能边界。
