背景
两个历史遗留问题需要解决:
- Bootstrap 检查分散问题:bootstrap 状态检查和自动解锁逻辑放在 FilesPage 中,导致我不在文件页时无法触发初始化
- 缩略图逻辑分散问题:缩略图相关逻辑分散在 file_tiles.dart、chat_page.dart 等多处,维护困难
Phase 1: Bootstrap 初始化迁移
问题分析
原有逻辑在 FilesPage 中:
_bootstrapChecked,_bootstrapChecking,_vaultStatus,_conflictCount都是 FilesPage 的本地变量_checkBootstrapStatus()方法在 FilesPage 中实现- 使用时必须进入文件页才能触发初始化和自动解锁
解决方案
将所有 bootstrap 相关状态和逻辑迁移到 AppState:
新增状态变量:
// app_state.dart
String? _vaultStatus; // 'ready', 'empty', 'conflict'
bool _bootstrapChecked = false;
bool _bootstrapChecking = false;
int? _conflictCount;新增方法:
/// 检查 bootstrap 状态并尝试自动解锁
Future<void> _checkBootstrapAndAutoUnlock() async {
if (_bootstrapChecked || _bootstrapChecking) return;
if (_isOffline) return;
if (_isUnlocked) return;
_bootstrapChecking = true;
notifyListeners();
// 调用 API 检查状态,尝试自动解锁...
}
/// 重置 bootstrap 状态(切换 S3 配置时)
void _resetBootstrapState() { ... }
/// 手动触发检查(供 UI 层调用)
Future<void> checkBootstrapIfNeeded() async { ... }集成点:
- 在
_checkInitialNetworkStatus()中调用_checkBootstrapAndAutoUnlock() - 在
switchToS3Config()中调用_resetBootstrapState()
FilesPage 清理
删除的内容:
- 本地状态变量:
_bootstrapChecked,_bootstrapChecking,_vaultStatus,_conflictCount - 方法:
_checkBootstrapStatus()
更新的内容:
_onStateChanged()改为调用appState.checkBootstrapIfNeeded()- UI 渲染改为读取
state.vaultStatus,state.bootstrapChecking,state.conflictCount
Phase 2: ThumbnailService 统一服务
问题分析
缩略图相关逻辑分散在多处:
file_tiles.dart:fetchOrGenerateThumbnail(),_generateVideoThumbnail()chat_page.dart:_regenerateThumbnail()- 类型判断重复:多处检查
isImage(),isVideo()
解决方案
创建 ThumbnailService 单例服务统一管理:
// core/services/thumbnail_service.dart
class ThumbnailService {
static final ThumbnailService _instance = ThumbnailService._();
factory ThumbnailService() => _instance;
// 类型判断
bool supportsThumbnail(String fileName) { ... }
bool isImage(String fileName) { ... }
bool isVideo(String fileName) { ... }
// 统一加载入口
Future<Uint8List?> loadThumbnail({
required String fileId,
String? fileName,
required ApiClient api,
bool autoGenImage = false,
bool autoGenVideo = false,
}) async {
// 1. 检查本地缓存
// 2. 从远程加载
// 3. 按需自动生成
}
// 视频缩略图生成
Future<Uint8List?> generateVideoThumbnailFromStream(...) { ... }
Future<Uint8List?> generateVideoThumbnailFromFile(...) { ... }
// 手动重新生成
Future<bool> regenerateThumbnail(...) { ... }
}调用方简化
file_tiles.dart:
Future<Uint8List?> fetchOrGenerateThumbnail(...) async {
return ThumbnailService().loadThumbnail(
fileId: fileId,
fileName: fileName,
api: api,
autoGenImage: autoGenImage,
autoGenVideo: autoGenVideo,
);
}chat_page.dart:
Future<void> _regenerateThumbnail(String fileId, String fileName) async {
await ThumbnailService().regenerateThumbnail(
fileId: fileId,
fileName: fileName,
api: appState.api,
);
}技术细节
缩略图加载流程
loadThumbnail() ├─ ThumbnailCacheManager.loadThumbnail() → 本地磁盘缓存 ├─ api.getThumbnailWithInfo(autoGen) → 远程加载 └─ _tryAutoGenerate() → 自动生成 ├─ 图片: 后端 autoGen=true 时按需生成 └─ 视频: 前端通过流式 URL 生成视频缩略图生成
使用 VideoThumbnailService 从流式 URL 生成:
Future<Uint8List?> generateVideoThumbnailFromStream({
required String fileId,
required ApiClient api,
}) async {
final streamUrl = api.getStreamUrl(fileId);
final token = api.getAuthToken();
final headers = <String, String>{};
if (token != null) headers['X-Auth-Token'] = token;
return VideoThumbnailService().generateFromUrl(streamUrl, headers);
}修改的文件
| 文件 | 操作 | 说明 |
|---|---|---|
app_state.dart | 修改 | 添加 bootstrap 状态管理 |
files_page.dart | 修改 | 清理重复逻辑,改用 AppState |
thumbnail_service.dart | 新增 | 统一缩略图服务 |
file_tiles.dart | 修改 | 简化为调用 ThumbnailService |
chat_page.dart | 修改 | 简化为调用 ThumbnailService |
验证结果
flutter analyzeAnalyzing client...No issues found!只剩 3 个预存的 info 级别警告,非本次引入。
收益
- 初始化更可靠:bootstrap 检查在 AppState 层面完成,不依赖特定页面
- 代码更集中:缩略图逻辑统一到 ThumbnailService,便于维护和扩展
- 职责更清晰:UI 层只需调用服务,不再关心底层实现细节