feat: reformat some pages

This commit is contained in:
2026-10-26 23:15:05 +01:00
parent 65e5e286b0
commit 08f4847800
17 changed files with 812 additions and 198 deletions
+48 -11
View File
@@ -6,23 +6,29 @@ import '../l10n/app_localizations_helpers.dart';
import '../models/app_error.dart';
import '../viewmodels/pin_lock_view_model.dart';
enum PinEntryMode { unlock, create }
enum PinEntryMode { unlock, create, change }
const _pinLength = 4;
/// Full-screen PIN entry. In [PinEntryMode.unlock] it verifies a PIN
/// against the one already saved. In [PinEntryMode.create] it walks the
/// person through entering a new PIN twice before saving it.
/// [PinEntryMode.change] first checks the current PIN, then does the same.
class PinEntryView extends StatefulWidget {
final PinEntryMode mode;
final VoidCallback? onSuccess;
final bool isAdmin;
final Future<bool> Function(String)? onVerify;
final Future<bool> Function(String)? onSet;
final Future<bool> Function(String currentPin, String newPin)? onChange;
final AppError? Function()? error;
/// Shows a close button that leaves the screen without entering a PIN.
final VoidCallback? onClose;
/// Replaces the title of the step that checks an existing PIN.
final String? title;
const PinEntryView({
super.key,
required this.mode,
@@ -30,8 +36,10 @@ class PinEntryView extends StatefulWidget {
this.isAdmin = false,
this.onVerify,
this.onSet,
this.onChange,
this.error,
this.onClose,
this.title,
});
@override
@@ -41,21 +49,33 @@ class PinEntryView extends StatefulWidget {
class _PinEntryViewState extends State<PinEntryView> {
String _digits = '';
String? _firstEntry;
String? _currentPin;
bool _isConfirmStep = false;
bool _isSubmitting = false;
String? _localError;
bool _shake = false;
bool get _isCreateFlow => widget.mode == PinEntryMode.create;
bool get _isChangeFlow => widget.mode == PinEntryMode.change;
bool get _isCreateFlow =>
widget.mode == PinEntryMode.create ||
(_isChangeFlow && _currentPin != null);
String _title(AppLocalizations l10n) {
if (widget.isAdmin) {
if (!_isCreateFlow) return l10n.enterAdminPin;
return _isConfirmStep ? l10n.confirmAdminPin : l10n.createAdminPin;
if (_isChangeFlow && _currentPin == null) {
return widget.title ??
(widget.isAdmin ? l10n.enterCurrentAdminPin : l10n.enterCurrentPin);
}
if (!_isCreateFlow) return l10n.enterPin;
return _isConfirmStep ? l10n.confirmPin : l10n.createPin;
if (widget.isAdmin) {
if (!_isCreateFlow) return widget.title ?? l10n.enterAdminPin;
if (_isConfirmStep) return l10n.confirmAdminPin;
return _isChangeFlow ? l10n.enterNewAdminPin : l10n.createAdminPin;
}
if (!_isCreateFlow) return widget.title ?? l10n.enterPin;
if (_isConfirmStep) return l10n.confirmPin;
return _isChangeFlow ? l10n.enterNewPin : l10n.createPin;
}
String? _subtitle(AppLocalizations l10n) {
@@ -114,7 +134,9 @@ class _PinEntryViewState extends State<PinEntryView> {
}
setState(() => _isSubmitting = true);
final ok = await _setPin(_digits);
final ok = _isChangeFlow
? await _changePin(_currentPin!, _digits)
: await _setPin(_digits);
if (!mounted) return;
setState(() => _isSubmitting = false);
@@ -135,10 +157,15 @@ class _PinEntryViewState extends State<PinEntryView> {
if (!mounted) return;
setState(() => _isSubmitting = false);
if (ok) {
widget.onSuccess?.call();
} else {
if (!ok) {
_fail(l10n.errorText(_error()));
} else if (_isChangeFlow) {
setState(() {
_currentPin = _digits;
_digits = '';
});
} else {
widget.onSuccess?.call();
}
}
@@ -149,6 +176,16 @@ class _PinEntryViewState extends State<PinEntryView> {
return context.read<PinLockViewModel>().setPin(pin);
}
Future<bool> _changePin(String currentPin, String newPin) {
final callback = widget.onChange;
if (callback != null) return callback(currentPin, newPin);
return context.read<PinLockViewModel>().changePin(
currentPin: currentPin,
newPin: newPin,
);
}
Future<bool> _verify(String pin) {
final callback = widget.onVerify;
if (callback != null) return callback(pin);