Flutter Android login vault: local DB (id+base64 content), email/Google auth, dashboard search/edit/swipe-delete, dark/system theme, minSdk 29

This commit is contained in:
cania
2026-08-12 11:41:20 +02:00
commit a8791a5b10
138 changed files with 6285 additions and 0 deletions

111
lib/settings_screen.dart Normal file
View File

@@ -0,0 +1,111 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:login_vault_app/auth_service.dart';
import 'package:login_vault_app/theme_provider.dart';
/// Settings: change app password, switch theme (dark/system), and Exit.
class SettingsScreen extends StatefulWidget {
final AuthService auth;
final ThemeProvider theme;
const SettingsScreen({super.key, required this.auth, required this.theme});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
final _oldCtrl = TextEditingController();
final _newCtrl = TextEditingController();
String? _msg;
Future<void> _changePassword() async {
final oldP = _oldCtrl.text;
final newP = _newCtrl.text;
if (newP.isEmpty) {
setState(() => _msg = 'Password baru wajib diisi');
return;
}
final stored = await widget.auth.getAppPassword();
if (stored != null && stored != '__google__' && stored != oldP) {
setState(() => _msg = 'Password lama salah');
return;
}
await widget.auth.changePassword(newP);
setState(() => _msg = 'Password aplikasi berhasil diubah');
_oldCtrl.clear();
_newCtrl.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Pengaturan')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text('Tema',
style: TextStyle(fontWeight: FontWeight.bold)),
ListTile(
title: const Text('Gelap (Dark)'),
leading: const Icon(Icons.dark_mode),
trailing: Radio<ThemeMode>(
value: ThemeMode.dark,
groupValue: widget.theme.mode,
onChanged: (m) => widget.theme.setMode(m!),
),
onTap: () => widget.theme.setMode(ThemeMode.dark),
),
ListTile(
title: const Text('Sistem (System)'),
leading: const Icon(Icons.brightness_auto),
trailing: Radio<ThemeMode>(
value: ThemeMode.system,
groupValue: widget.theme.mode,
onChanged: (m) => widget.theme.setMode(m!),
),
onTap: () => widget.theme.setMode(ThemeMode.system),
),
const Divider(),
const Text('Ubah Password Aplikasi',
style: TextStyle(fontWeight: FontWeight.bold)),
TextField(
controller: _oldCtrl,
obscureText: true,
decoration: const InputDecoration(labelText: 'Password lama'),
),
TextField(
controller: _newCtrl,
obscureText: true,
decoration: const InputDecoration(labelText: 'Password baru'),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _changePassword,
child: const Text('Simpan Password'),
),
if (_msg != null)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(_msg!,
style: const TextStyle(color: Colors.green)),
),
const Divider(),
ListTile(
leading: const Icon(Icons.exit_to_app),
title: const Text('Keluar (Exit)'),
onTap: () {
// On Android, pop the whole stack then ask the system to close.
Navigator.of(context).popUntil((r) => r.isFirst);
if (Platform.isAndroid) {
SystemNavigator.pop();
} else {
exit(0);
}
},
),
],
),
);
}
}