Add local backup/restore: encrypted .enc export to Download/loginvault, restore from list (no file_picker to avoid SDK conflict)

This commit is contained in:
cania
2026-08-13 14:47:14 +02:00
parent 92a3ca4d82
commit 7cb2ca42c5
8 changed files with 256 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:login_vault_app/auth_service.dart';
import 'package:login_vault_app/auth_screen.dart';
import 'package:login_vault_app/theme_provider.dart';
import 'package:login_vault_app/backup_service.dart';
/// Settings: change app password, switch theme (dark/system), session timeout,
/// fingerprint toggle, and Exit.
@@ -72,6 +73,73 @@ class _SettingsScreenState extends State<SettingsScreen> {
_newCtrl.clear();
}
Future<void> _backup() async {
try {
final path = await BackupService.exportEncrypted(widget.auth);
if (mounted) {
setState(() {
_msg = 'Backup tersimpan: $path';
_msgColor = Colors.green;
});
}
} catch (e) {
if (mounted) {
setState(() {
_msg = 'Backup gagal: $e';
_msgColor = Colors.red;
});
}
}
}
Future<void> _restore() async {
try {
final backups = await BackupService.listBackups();
if (backups.isEmpty) {
if (mounted) {
setState(() {
_msg = 'Tidak ada file backup di folder Download/loginvault';
_msgColor = Colors.red;
});
}
return;
}
if (!mounted) return;
final chosen = await showDialog<String>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Pilih file backup'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: backups.length,
itemBuilder: (_, i) => ListTile(
title: Text(backups[i].path.split('/').last),
onTap: () => Navigator.of(ctx).pop(backups[i].path),
),
),
),
),
);
if (chosen == null) return;
final count = await BackupService.importEncrypted(widget.auth, chosen);
if (mounted) {
setState(() {
_msg = 'Restore berhasil: $count entri';
_msgColor = Colors.green;
});
}
} catch (e) {
if (mounted) {
setState(() {
_msg = 'Restore gagal: $e';
_msgColor = Colors.red;
});
}
}
}
Future<void> _toggleBiometric(bool value) async {
if (value && !_biometricAvailable) {
setState(() {
@@ -209,6 +277,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Text(_msg!, style: TextStyle(color: _msgColor)),
),
const Divider(),
const Text('Backup & Restore',
style: TextStyle(fontWeight: FontWeight.bold)),
ListTile(
leading: const Icon(Icons.backup),
title: const Text('Backup ke file (lokal)'),
subtitle: const Text(
'Simpan vault terenkripsi ke folder Download. Bisa dipindah ke Drive manual.'),
onTap: _backup,
),
ListTile(
leading: const Icon(Icons.restore),
title: const Text('Restore dari file'),
subtitle: const Text('Pilih file backup .enc untuk dikembalikan.'),
onTap: _restore,
),
const Divider(),
ListTile(
leading: const Icon(Icons.exit_to_app),
title: const Text('Keluar (Exit)'),