feat(app): BLoC 集成 Repository + SettingsBloc 主题切换
全局依赖注入: - app.dart 注入 JournalRepository + ClassRepository + SettingsBloc - ApiClient token 自动注入(监听 AuthBloc 状态) BLoC 重构 (占位数据 → Repository): - CalendarBloc: 通过 JournalRepository 加载月度日记 - ClassBloc: 通过 ClassRepository + JournalRepository 加载班级数据 - 新增 ClassJoin 事件支持班级码加入 - HomeBloc: 加载最近日记 + 心情概览 + 连续天数 + 今日是否已写 设置系统: - SettingsBloc: ThemeMode 切换 (system/light/dark) - app.dart 通过 ListenableBuilder 响应主题变化 - HomeBloc 支持下拉刷新 首页增强: - 连续天数徽章 + 今日已写标记 + 最常用心情高亮 - RefreshIndicator 下拉刷新 - 日记列表卡片显示日期 验证: flutter analyze 0 error
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
// 日历 BLoC — 管理日历视图状态和日记列表
|
||||
// 日历 BLoC — 管理日历视图状态,通过 JournalRepository 加载数据
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:nuanji_app/data/models/journal_entry.dart';
|
||||
import 'package:nuanji_app/data/repositories/journal_repository.dart';
|
||||
|
||||
// ===== Events =====
|
||||
|
||||
@@ -21,18 +22,12 @@ final class CalendarDaySelected extends CalendarEvent {
|
||||
const CalendarDaySelected(this.day);
|
||||
}
|
||||
|
||||
/// 切换视图模式(月/周/时间轴)
|
||||
/// 切换视图模式
|
||||
final class CalendarViewModeChanged extends CalendarEvent {
|
||||
final CalendarViewMode mode;
|
||||
const CalendarViewModeChanged(this.mode);
|
||||
}
|
||||
|
||||
/// 加载某月的日记列表
|
||||
final class CalendarLoadJournals extends CalendarEvent {
|
||||
final DateTime month;
|
||||
const CalendarLoadJournals(this.month);
|
||||
}
|
||||
|
||||
// ===== State =====
|
||||
|
||||
/// 日历视图模式
|
||||
@@ -43,29 +38,17 @@ sealed class CalendarState {
|
||||
const CalendarState();
|
||||
}
|
||||
|
||||
/// 初始加载中
|
||||
final class CalendarInitial extends CalendarState {
|
||||
const CalendarInitial();
|
||||
}
|
||||
|
||||
/// 日历已加载 — 包含当前月份、选中日期、日记列表
|
||||
/// 日历已加载
|
||||
final class CalendarLoaded extends CalendarState {
|
||||
/// 当前显示的月份
|
||||
final DateTime focusedMonth;
|
||||
|
||||
/// 选中的日期
|
||||
final DateTime selectedDay;
|
||||
|
||||
/// 当前月份所有日记(按日期索引)
|
||||
final Map<DateTime, List<JournalEntry>> journalsByDate;
|
||||
|
||||
/// 当前选中日期的日记列表
|
||||
final List<JournalEntry> selectedDayJournals;
|
||||
|
||||
/// 视图模式
|
||||
final CalendarViewMode viewMode;
|
||||
|
||||
/// 是否正在加载
|
||||
final bool isLoading;
|
||||
|
||||
const CalendarLoaded({
|
||||
@@ -95,7 +78,6 @@ final class CalendarLoaded extends CalendarState {
|
||||
);
|
||||
}
|
||||
|
||||
/// 加载失败
|
||||
final class CalendarError extends CalendarState {
|
||||
final String message;
|
||||
const CalendarError(this.message);
|
||||
@@ -104,17 +86,20 @@ final class CalendarError extends CalendarState {
|
||||
// ===== BLoC =====
|
||||
|
||||
class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
CalendarBloc() : super(const CalendarInitial()) {
|
||||
final JournalRepository _journalRepo;
|
||||
|
||||
CalendarBloc({required JournalRepository journalRepository})
|
||||
: _journalRepo = journalRepository,
|
||||
super(const CalendarInitial()) {
|
||||
on<CalendarMonthChanged>(_onMonthChanged);
|
||||
on<CalendarDaySelected>(_onDaySelected);
|
||||
on<CalendarViewModeChanged>(_onViewModeChanged);
|
||||
on<CalendarLoadJournals>(_onLoadJournals);
|
||||
}
|
||||
|
||||
void _onMonthChanged(
|
||||
Future<void> _onMonthChanged(
|
||||
CalendarMonthChanged event,
|
||||
Emitter<CalendarState> emit,
|
||||
) {
|
||||
) async {
|
||||
final currentState = state is CalendarLoaded ? state as CalendarLoaded : null;
|
||||
|
||||
emit(CalendarLoaded(
|
||||
@@ -123,9 +108,38 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
journalsByDate: currentState?.journalsByDate ?? {},
|
||||
selectedDayJournals: [],
|
||||
viewMode: currentState?.viewMode ?? CalendarViewMode.month,
|
||||
isLoading: true,
|
||||
));
|
||||
|
||||
add(CalendarLoadJournals(event.month));
|
||||
try {
|
||||
// 加载当月日记
|
||||
final startOfMonth = DateTime(event.month.year, event.month.month, 1);
|
||||
final endOfMonth = DateTime(event.month.year, event.month.month + 1, 0);
|
||||
|
||||
final journals = await _journalRepo.getJournals(
|
||||
dateFrom: startOfMonth,
|
||||
dateTo: endOfMonth,
|
||||
);
|
||||
|
||||
// 按日期索引
|
||||
final byDate = <DateTime, List<JournalEntry>>{};
|
||||
for (final journal in journals) {
|
||||
final key = DateTime(journal.date.year, journal.date.month, journal.date.day);
|
||||
byDate.putIfAbsent(key, () => []).add(journal);
|
||||
}
|
||||
|
||||
if (state is CalendarLoaded) {
|
||||
final current = state as CalendarLoaded;
|
||||
emit(current.copyWith(
|
||||
journalsByDate: byDate,
|
||||
isLoading: false,
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
if (state is CalendarLoaded) {
|
||||
emit((state as CalendarLoaded).copyWith(isLoading: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onDaySelected(
|
||||
@@ -135,7 +149,6 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
if (state is! CalendarLoaded) return;
|
||||
final current = state as CalendarLoaded;
|
||||
|
||||
// 查找选中日期的日记
|
||||
final dayKey = DateTime(event.day.year, event.day.month, event.day.day);
|
||||
final dayJournals = current.journalsByDate[dayKey] ?? [];
|
||||
|
||||
@@ -150,26 +163,6 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
Emitter<CalendarState> emit,
|
||||
) {
|
||||
if (state is! CalendarLoaded) return;
|
||||
final current = state as CalendarLoaded;
|
||||
emit(current.copyWith(viewMode: event.mode));
|
||||
}
|
||||
|
||||
Future<void> _onLoadJournals(
|
||||
CalendarLoadJournals event,
|
||||
Emitter<CalendarState> emit,
|
||||
) async {
|
||||
if (state is! CalendarLoaded) return;
|
||||
final current = state as CalendarLoaded;
|
||||
|
||||
emit(current.copyWith(isLoading: true));
|
||||
|
||||
// Phase 1: 使用空数据占位,待 Repository 集成后替换
|
||||
// 实际将从 JournalRepository.loadByMonth(event.month) 获取
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
emit(current.copyWith(
|
||||
isLoading: false,
|
||||
journalsByDate: current.journalsByDate,
|
||||
));
|
||||
emit((state as CalendarLoaded).copyWith(viewMode: event.mode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:nuanji_app/core/theme/app_colors.dart';
|
||||
import 'package:nuanji_app/data/models/journal_entry.dart';
|
||||
import 'package:nuanji_app/data/repositories/journal_repository.dart';
|
||||
import '../bloc/calendar_bloc.dart';
|
||||
|
||||
/// 日历页面 — 月视图 + 选中日期的日记列表
|
||||
@@ -14,8 +15,9 @@ class CalendarPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => CalendarBloc()
|
||||
..add(CalendarMonthChanged(DateTime.now())),
|
||||
create: (context) => CalendarBloc(
|
||||
journalRepository: context.read<JournalRepository>(),
|
||||
)..add(CalendarMonthChanged(DateTime.now())),
|
||||
child: const _CalendarView(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user