added base homescreen-widget setup, working on Android, iOS in progress

This commit is contained in:
2026-05-09 18:01:05 +02:00
parent 0ff5eb7bc9
commit 00664c66a8
66 changed files with 5600 additions and 4 deletions
+77
View File
@@ -0,0 +1,77 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../state/app/modules/timetable/bloc/timetable_state.dart';
import '../storage/settings.dart';
import 'widget_data_mapper.dart';
import 'widget_sync.dart';
/// Pushes timetable state to the native widget whenever the foreground bloc
/// has fresh data, so the widget doesn't have to wait for the next periodic
/// background fetch.
class WidgetPublisher {
/// Debug-only "now" offset. Gated by [kDebugMode] so a stray non-zero
/// value cannot ship in release.
static const Duration debugTimeShift = Duration.zero;
static DateTime widgetNow() =>
kDebugMode ? DateTime.now().add(debugTimeShift) : DateTime.now();
static Future<void> publishFromBlocState(
TimetableState state, {
Settings? settings,
}) async {
try {
final connectDouble =
settings?.timetableSettings.connectDoubleLessons ?? true;
// Mirror into widget storage so the background isolate sees the same
// value the user just toggled.
await WidgetSync.setConnectDoubleLessons(connectDouble);
await WidgetSync.setThemeMode(_themeName(settings?.appTheme));
final lessons = state.getAllKnownLessons();
final now = widgetNow();
final dayData = WidgetDataMapper.buildDayData(
now: now,
lessons: lessons,
subjects: state.subjects,
rooms: state.rooms,
holidays: state.schoolHolidays,
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
);
final weekData = WidgetDataMapper.buildWeekData(
now: now,
lessons: lessons,
subjects: state.subjects,
rooms: state.rooms,
holidays: state.schoolHolidays,
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
);
await WidgetSync.writeDayData(dayData);
await WidgetSync.writeWeekData(weekData);
await WidgetSync.setLoggedIn(true);
await WidgetSync.triggerUpdate();
} on Object catch (e, s) {
// Catch Object: non-Exception Errors (RangeError, StateError) from the
// bloc layer must not escape into the stream listener.
log('WidgetPublisher.publishFromBlocState failed: $e', stackTrace: s);
}
}
static String _themeName(ThemeMode? mode) {
switch (mode) {
case ThemeMode.light:
return 'light';
case ThemeMode.dark:
return 'dark';
case ThemeMode.system:
case null:
return 'system';
}
}
}