diff --git a/lib/providers/app_lock_provider.dart b/lib/providers/app_lock_provider.dart index c5ea650..cbc6266 100644 --- a/lib/providers/app_lock_provider.dart +++ b/lib/providers/app_lock_provider.dart @@ -67,6 +67,12 @@ class AppLockProvider extends ChangeNotifier { } try { + // async 전에 context 기반 데이터 미리 획득 + final ctx = navigatorKey.currentContext; + final loc = ctx != null ? AppLocalizations.of(ctx) : null; + final localizedReason = + loc?.unlockWithBiometric ?? 'Unlock with biometric authentication.'; + final canCheck = await _checkBiometrics(); if (!canCheck) { _isLocked = false; @@ -74,11 +80,8 @@ class AppLockProvider extends ChangeNotifier { return true; } - final ctx = navigatorKey.currentContext; - final loc = ctx != null ? AppLocalizations.of(ctx) : null; final authenticated = await _localAuth.authenticate( - localizedReason: - loc?.unlockWithBiometric ?? 'Unlock with biometric authentication.', + localizedReason: localizedReason, options: const AuthenticationOptions( stickyAuth: true, biometricOnly: true, diff --git a/lib/widgets/common/dialogs/confirmation_dialog.dart b/lib/widgets/common/dialogs/confirmation_dialog.dart deleted file mode 100644 index 75693ad..0000000 --- a/lib/widgets/common/dialogs/confirmation_dialog.dart +++ /dev/null @@ -1,358 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../theme/color_scheme_ext.dart'; - -/// 확인 다이얼로그 위젯 -/// 사용자에게 중요한 작업을 확인받을 때 사용합니다. -class ConfirmationDialog extends StatelessWidget { - final String title; - final String? message; - final Widget? content; - final String confirmText; - final String cancelText; - final VoidCallback? onConfirm; - final VoidCallback? onCancel; - final Color? confirmColor; - final IconData? icon; - final Color? iconColor; - final double iconSize; - - const ConfirmationDialog({ - super.key, - required this.title, - this.message, - this.content, - this.confirmText = '확인', - this.cancelText = '취소', - this.onConfirm, - this.onCancel, - this.confirmColor, - this.icon, - this.iconColor, - this.iconSize = 48, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final effectiveConfirmColor = confirmColor ?? theme.primaryColor; - - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - title: Text( - title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (icon != null) ...[ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: - (iconColor ?? effectiveConfirmColor).withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(12), - ), - child: Icon( - icon, - color: iconColor ?? effectiveConfirmColor, - size: iconSize, - ), - ), - const SizedBox(height: 16), - ], - if (content != null) - content! - else if (message != null) - Text( - message!, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - height: 1.5, - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(false); - onCancel?.call(); - }, - child: Text(cancelText), - ), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(true); - onConfirm?.call(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: effectiveConfirmColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: Text( - confirmText, - style: TextStyle( - color: Theme.of(context).colorScheme.onPrimary, - ), - ), - ), - ], - ); - } - - /// 다이얼로그를 표시하고 결과를 반환하는 정적 메서드 - static Future show({ - required BuildContext context, - required String title, - String? message, - Widget? content, - String confirmText = '확인', - String cancelText = '취소', - Color? confirmColor, - IconData? icon, - Color? iconColor, - double iconSize = 48, - }) { - return showDialog( - context: context, - barrierDismissible: false, - builder: (context) => ConfirmationDialog( - title: title, - message: message, - content: content, - confirmText: confirmText, - cancelText: cancelText, - confirmColor: confirmColor, - icon: icon, - iconColor: iconColor, - iconSize: iconSize, - ), - ); - } -} - -/// 성공 다이얼로그 -class SuccessDialog extends StatelessWidget { - final String title; - final String? message; - final String buttonText; - final VoidCallback? onPressed; - - const SuccessDialog({ - super.key, - required this.title, - this.message, - this.buttonText = '확인', - this.onPressed, - }); - - @override - Widget build(BuildContext context) { - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: - Theme.of(context).colorScheme.success.withValues(alpha: 0.1), - shape: BoxShape.circle, - ), - child: Icon( - Icons.check_circle, - color: Theme.of(context).colorScheme.success, - size: 64, - ), - ), - const SizedBox(height: 24), - Text( - title, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - if (message != null) ...[ - const SizedBox(height: 12), - Text( - message!, - style: TextStyle( - fontSize: 16, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - textAlign: TextAlign.center, - ), - ], - ], - ), - actions: [ - Center( - child: ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - onPressed?.call(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.success, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric( - horizontal: 32, - vertical: 12, - ), - ), - child: Text( - buttonText, - style: TextStyle( - color: Theme.of(context).colorScheme.onPrimary, - fontSize: 16, - ), - ), - ), - ), - ], - ); - } - - static Future show({ - required BuildContext context, - required String title, - String? message, - String buttonText = '확인', - VoidCallback? onPressed, - }) { - return showDialog( - context: context, - barrierDismissible: false, - builder: (context) => SuccessDialog( - title: title, - message: message, - buttonText: buttonText, - onPressed: onPressed, - ), - ); - } -} - -/// 에러 다이얼로그 -class ErrorDialog extends StatelessWidget { - final String title; - final String? message; - final String buttonText; - final VoidCallback? onPressed; - - const ErrorDialog({ - super.key, - required this.title, - this.message, - this.buttonText = '확인', - this.onPressed, - }); - - @override - Widget build(BuildContext context) { - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.error.withValues(alpha: 0.1), - shape: BoxShape.circle, - ), - child: Icon( - Icons.error_outline, - color: Theme.of(context).colorScheme.error, - size: 64, - ), - ), - const SizedBox(height: 24), - Text( - title, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - if (message != null) ...[ - const SizedBox(height: 12), - Text( - message!, - style: TextStyle( - fontSize: 16, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - textAlign: TextAlign.center, - ), - ], - ], - ), - actions: [ - Center( - child: ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - onPressed?.call(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.error, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric( - horizontal: 32, - vertical: 12, - ), - ), - child: Text( - buttonText, - style: TextStyle( - color: Theme.of(context).colorScheme.onPrimary, - fontSize: 16, - ), - ), - ), - ), - ], - ); - } - - static Future show({ - required BuildContext context, - required String title, - String? message, - String buttonText = '확인', - VoidCallback? onPressed, - }) { - return showDialog( - context: context, - barrierDismissible: false, - builder: (context) => ErrorDialog( - title: title, - message: message, - buttonText: buttonText, - onPressed: onPressed, - ), - ); - } -}