Files
nj/app/lib/app.dart
iven 2481c8fce6 feat(app): Isar 本地数据库集成 — Collection + Repository + 编辑器持久化 + SyncEngine 队列
新增文件:
- data/local/collections/ 3 个 Isar Collection 定义 + 生成 Schema
- data/repositories/isar_journal_repository.dart 完整 CRUD + 乐观锁

修改文件:
- app.dart: IsarJournalRepository 注册为主 JournalRepository + SyncEngine 注入
- editor_page.dart: onSave 接入 JournalRepository,笔画/元素自动保存到 Isar
- sync_engine.dart: 新增 persistPendingQueue/restorePendingQueue Isar 持久化
- isar_database.dart: 注册 3 个 Collection Schema
- main.dart: 启动时初始化 Isar

架构: 离线优先 — Isar 为本地主仓库,Remote 供 SyncEngine 推送
2026-06-01 14:41:40 +08:00

105 lines
3.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 暖记 App 根组件 — MaterialApp + BLoC Provider 注入
//
// 依赖注入结构:
// MultiRepositoryProvider
// ├─ ApiClient
// ├─ AuthRepository
// ├─ JournalRepository (IsarJournalRepository — 离线优先)
// ├─ RemoteJournalRepository (供 SyncEngine 使用)
// └─ ClassRepository
// └─ BlocProvider<AuthBloc>
// └─ MaterialApp.router
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'core/theme/app_theme.dart';
import 'core/routing/app_router.dart';
import 'data/remote/api_client.dart';
import 'data/repositories/auth_repository.dart';
import 'data/repositories/journal_repository.dart';
import 'data/repositories/isar_journal_repository.dart';
import 'data/repositories/remote_journal_repository.dart';
import 'data/repositories/class_repository.dart';
import 'data/services/sync_engine.dart';
import 'features/auth/bloc/auth_bloc.dart';
import 'features/profile/bloc/settings_bloc.dart';
/// 暖记 App — 根组件
class NuanjiApp extends StatelessWidget {
const NuanjiApp({super.key});
@override
Widget build(BuildContext context) {
// 创建全局依赖App 生命周期内单例)
final apiClient = ApiClient();
final authRepository = AuthRepository(apiClient: apiClient);
// 离线优先Isar 为主要本地仓库Remote 供 SyncEngine 推送
final journalRepository = IsarJournalRepository();
final remoteJournalRepository = RemoteJournalRepository(api: apiClient);
final syncEngine = SyncEngine(apiClient: apiClient);
final classRepository = ClassRepository(api: apiClient);
final settingsBloc = SettingsBloc();
final authBloc = AuthBloc(authRepository: authRepository);
// 启动时检查认证状态
authBloc.add(const AppStarted());
// 异步恢复 SyncEngine 持久化队列fire-and-forget不阻塞 UI
syncEngine.restorePendingQueue();
// 认证成功后注入 JWT token 到 ApiClient
authBloc.stream.listen((state) {
if (state is Authenticated) {
// TODO: 从 SecureStorage 读取 token 并设置
// apiClient.setToken(token);
} else {
apiClient.clearToken();
}
});
return MultiRepositoryProvider(
providers: [
RepositoryProvider<ApiClient>.value(value: apiClient),
RepositoryProvider<AuthRepository>.value(value: authRepository),
RepositoryProvider<JournalRepository>.value(value: journalRepository),
RepositoryProvider<RemoteJournalRepository>.value(value: remoteJournalRepository),
RepositoryProvider<SyncEngine>.value(value: syncEngine),
RepositoryProvider<ClassRepository>.value(value: classRepository),
RepositoryProvider<SettingsBloc>.value(value: settingsBloc),
],
child: BlocProvider<AuthBloc>.value(
value: authBloc,
child: ListenableBuilder(
listenable: settingsBloc,
builder: (context, _) => _AppView(
router: createAppRouter(authBloc),
themeMode: settingsBloc.state.themeMode,
),
),
),
);
}
}
/// App 视图 — MaterialApp.router 包装
class _AppView extends StatelessWidget {
final GoRouter router;
final ThemeMode themeMode;
const _AppView({required this.router, this.themeMode = ThemeMode.system});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: '暖记',
debugShowCheckedModeBanner: false,
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: themeMode,
routerConfig: router,
);
}
}