rwadurian/frontend/mining-app/lib/presentation/widgets/trade_password_dialog.dart

244 lines
7.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/constants/app_colors.dart';
import '../providers/user_providers.dart';
/// 支付密码验证弹窗
/// 返回 true 表示验证通过false 或 null 表示取消或验证失败
class TradePasswordDialog extends ConsumerStatefulWidget {
final String title;
final String? subtitle;
const TradePasswordDialog({
super.key,
this.title = '请输入支付密码',
this.subtitle,
});
@override
ConsumerState<TradePasswordDialog> createState() => _TradePasswordDialogState();
/// 显示支付密码验证弹窗
/// 返回 true 表示验证通过false 或 null 表示取消或验证失败
static Future<bool?> show(
BuildContext context, {
String title = '请输入支付密码',
String? subtitle,
}) {
return showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => TradePasswordDialog(
title: title,
subtitle: subtitle,
),
);
}
}
class _TradePasswordDialogState extends ConsumerState<TradePasswordDialog> {
final _passwordController = TextEditingController();
final _focusNode = FocusNode();
bool _obscurePassword = true;
bool _isVerifying = false;
String? _errorMessage;
@override
void initState() {
super.initState();
// 自动聚焦密码输入框
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
}
@override
void dispose() {
_passwordController.dispose();
_focusNode.dispose();
super.dispose();
}
Future<void> _verify() async {
final password = _passwordController.text;
if (password.isEmpty) {
setState(() => _errorMessage = '请输入支付密码');
return;
}
if (password.length != 6) {
setState(() => _errorMessage = '支付密码必须是6位数字');
return;
}
setState(() {
_isVerifying = true;
_errorMessage = null;
});
try {
final isValid = await ref.read(userNotifierProvider.notifier).verifyTradePassword(password);
if (mounted) {
if (isValid) {
Navigator.of(context).pop(true);
} else {
setState(() {
_isVerifying = false;
_errorMessage = '支付密码错误';
_passwordController.clear();
});
}
}
} catch (e) {
if (mounted) {
setState(() {
_isVerifying = false;
_errorMessage = '验证失败,请重试';
});
}
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
Icons.lock_outline,
color: AppColors.primary,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
if (widget.subtitle != null) ...[
const SizedBox(height: 4),
Text(
widget.subtitle!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.normal,
),
),
],
],
),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _passwordController,
focusNode: _focusNode,
obscureText: _obscurePassword,
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
letterSpacing: 8,
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(6),
],
decoration: InputDecoration(
hintText: '请输入6位数字',
hintStyle: TextStyle(
fontSize: 16,
color: Colors.grey[400],
letterSpacing: 0,
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility_off : Icons.visibility,
color: Colors.grey[600],
),
onPressed: () => setState(() => _obscurePassword = !_obscurePassword),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primary, width: 2),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
),
onSubmitted: (_) => _verify(),
),
if (_errorMessage != null) ...[
const SizedBox(height: 12),
Row(
children: [
Icon(Icons.error_outline, color: AppColors.error, size: 16),
const SizedBox(width: 4),
Text(
_errorMessage!,
style: TextStyle(
color: AppColors.error,
fontSize: 14,
),
),
],
),
],
],
),
actions: [
TextButton(
onPressed: _isVerifying ? null : () => Navigator.of(context).pop(false),
child: const Text('取消'),
),
ElevatedButton(
onPressed: _isVerifying ? null : _verify,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isVerifying
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'确认',
style: TextStyle(color: Colors.white),
),
),
],
);
}
}