Fix fingerprint: adaptive auth screen (login/register by state), auto-prompt biometric on open, optimistic switch, real error messages
This commit is contained in:
@@ -19,6 +19,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final _oldCtrl = TextEditingController();
|
||||
final _newCtrl = TextEditingController();
|
||||
String? _msg;
|
||||
Color _msgColor = Colors.green;
|
||||
bool _biometricOn = false;
|
||||
bool _biometricAvailable = false;
|
||||
int _timeout = 3;
|
||||
@@ -48,35 +49,53 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final oldP = _oldCtrl.text;
|
||||
final newP = _newCtrl.text;
|
||||
if (newP.isEmpty) {
|
||||
setState(() => _msg = 'Password baru wajib diisi');
|
||||
setState(() {
|
||||
_msg = 'Password baru wajib diisi';
|
||||
_msgColor = Colors.red;
|
||||
});
|
||||
return;
|
||||
}
|
||||
final stored = await widget.auth.getAppPassword();
|
||||
if (stored != null && stored != '__google__' && stored != oldP) {
|
||||
setState(() => _msg = 'Password lama salah');
|
||||
setState(() {
|
||||
_msg = 'Password lama salah';
|
||||
_msgColor = Colors.red;
|
||||
});
|
||||
return;
|
||||
}
|
||||
await widget.auth.changePassword(newP);
|
||||
setState(() => _msg = 'Password aplikasi berhasil diubah');
|
||||
setState(() {
|
||||
_msg = 'Password aplikasi berhasil diubah';
|
||||
_msgColor = Colors.green;
|
||||
});
|
||||
_oldCtrl.clear();
|
||||
_newCtrl.clear();
|
||||
}
|
||||
|
||||
Future<void> _toggleBiometric(bool value) async {
|
||||
if (value && !_biometricAvailable) {
|
||||
setState(() => _msg = 'Perangkat tidak mendukung biometrik');
|
||||
setState(() {
|
||||
_msg = 'Perangkat tidak mendukung biometrik';
|
||||
_msgColor = Colors.red;
|
||||
});
|
||||
return;
|
||||
}
|
||||
// optimistic: move switch immediately so it feels responsive
|
||||
if (mounted) setState(() => _biometricOn = value);
|
||||
if (value) {
|
||||
String? err;
|
||||
final ok = await widget.auth.authenticateWithBiometrics(errorOut: err);
|
||||
final (ok, err) = await widget.auth.authenticateWithBiometrics();
|
||||
if (!ok) {
|
||||
setState(() => _msg = 'Verifikasi biometrik gagal: ${err ?? "dibatalkan"}');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_biometricOn = false;
|
||||
_msg = 'Verifikasi biometrik gagal: ${err ?? "dibatalkan"}';
|
||||
_msgColor = Colors.red;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
await widget.auth.setBiometricEnabled(value);
|
||||
if (mounted) setState(() => _biometricOn = value);
|
||||
}
|
||||
|
||||
Future<void> _setTimeout(int? value) async {
|
||||
@@ -121,12 +140,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ListTile(
|
||||
title: const Text('Timeout (tanpa aktivitas)'),
|
||||
subtitle: Text('Logout otomatis setelah: ${_timeoutLabel(_timeout)}'),
|
||||
trailing: DropdownButton<int>(
|
||||
value: _timeout,
|
||||
items: _timeoutOptions
|
||||
.map((m) => DropdownMenuItem(value: m, child: Text(_timeoutLabel(m))))
|
||||
trailing: PopupMenuButton<int>(
|
||||
initialValue: _timeoutOptions.contains(_timeout) ? _timeout : _timeoutOptions.first,
|
||||
onSelected: _setTimeout,
|
||||
itemBuilder: (ctx) => _timeoutOptions
|
||||
.map((m) => PopupMenuItem(value: m, child: Text(_timeoutLabel(m))))
|
||||
.toList(),
|
||||
onChanged: _setTimeout,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(_timeoutLabel(_timeout)),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
@@ -158,7 +184,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (_msg != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(_msg!, style: const TextStyle(color: Colors.green)),
|
||||
child: Text(_msg!, style: TextStyle(color: _msgColor)),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
|
||||
Reference in New Issue
Block a user