A silent safety net against in-app data loss or DB corruption, with zero friction. Driven off the app lifecycle (startup + when hidden), it writes a sealed .tanemaki copy into the app's private storage once a week, keeping the newest 3 (rotating). Reuses the manual backup's encryption via the extracted ExportImportService.buildSealedBackup(); never throws, so a failed copy can't crash startup or backgrounding. Not registered on web (no file storage). Settings shows a quiet reassurance line with the date of the last automatic copy (localised via intl), hidden where no automatic backup exists.
42 lines
1.3 KiB
Dart
42 lines
1.3 KiB
Dart
import 'package:flutter/widgets.dart';
|
|
|
|
import '../services/auto_backup_service.dart';
|
|
|
|
/// Drives the automatic backup off the app lifecycle: once on startup and again
|
|
/// each time the app becomes hidden (backgrounded on mobile, window hidden on
|
|
/// desktop). The [AutoBackupService] decides whether a copy is actually due, so
|
|
/// these are cheap no-ops most of the time.
|
|
///
|
|
/// When [service] is null (widget tests, or platforms without file storage such
|
|
/// as web) it wraps [child] untouched and does nothing.
|
|
class AutoBackupGate extends StatefulWidget {
|
|
const AutoBackupGate({required this.child, this.service, super.key});
|
|
|
|
final Widget child;
|
|
final AutoBackupService? service;
|
|
|
|
@override
|
|
State<AutoBackupGate> createState() => _AutoBackupGateState();
|
|
}
|
|
|
|
class _AutoBackupGateState extends State<AutoBackupGate> {
|
|
AppLifecycleListener? _listener;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
final service = widget.service;
|
|
if (service == null) return;
|
|
_listener = AppLifecycleListener(onHide: () => service.runIfDue());
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => service.runIfDue());
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_listener?.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => widget.child;
|
|
}
|