Files
kuaishai2/lib/features/settings/widgets/password_panel.dart
Developer 67e2c7c76c refactor(settings): 语言/密码/U盘导入改用右侧内嵌面板
- 新增 LanguagePanel / PasswordPanel / UsbImportPanel 三个 widget
- settings_page 移除 AlertDialog 弹窗逻辑
- 5 个菜单项统一走 _buildContent() switch 切换右侧内容
- 验证:flutter analyze 无新增 issue
2026-06-04 15:10:03 +08:00

196 lines
5.8 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_riverpod/flutter_riverpod.dart';
import '../../../core/theme/app_theme.dart';
import '../../../shared/services/toast_service.dart';
import '../services/settings_service.dart';
/// 密码修改面板
///
/// 在设置页右侧主区域渲染密码修改表单,行为与原弹窗一致:
/// 校验空值、长度≥6、两次一致性、原密码正确性。
class PasswordPanel extends ConsumerStatefulWidget {
const PasswordPanel({super.key});
@override
ConsumerState<PasswordPanel> createState() => _PasswordPanelState();
}
class _PasswordPanelState extends ConsumerState<PasswordPanel> {
final _oldCtrl = TextEditingController();
final _newCtrl = TextEditingController();
final _confirmCtrl = TextEditingController();
bool _submitting = false;
String? _error;
@override
void dispose() {
_oldCtrl.dispose();
_newCtrl.dispose();
_confirmCtrl.dispose();
super.dispose();
}
Future<void> _submit() async {
if (_submitting) return;
final oldPwd = _oldCtrl.text.trim();
final newPwd = _newCtrl.text.trim();
final confirmPwd = _confirmCtrl.text.trim();
if (oldPwd.isEmpty || newPwd.isEmpty || confirmPwd.isEmpty) {
setState(() => _error = '请填写所有字段');
return;
}
if (newPwd.length < 6) {
setState(() => _error = '新密码至少6位字符');
return;
}
if (newPwd != confirmPwd) {
setState(() => _error = '两次输入的新密码不一致');
return;
}
setState(() {
_submitting = true;
_error = null;
});
final isValid = await SettingsService.instance.verifyPassword(oldPwd);
if (!isValid) {
if (!mounted) return;
setState(() {
_submitting = false;
_error = '原密码错误';
});
return;
}
final ok = await SettingsService.instance.setPassword(newPwd);
if (!mounted) return;
setState(() => _submitting = false);
if (ok) {
_oldCtrl.clear();
_newCtrl.clear();
_confirmCtrl.clear();
ToastService.showSuccess(context, '密码已修改');
} else {
ToastService.showError(context, '密码修改失败');
}
}
@override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.zero,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppTheme.borderSubtle),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'密码修改',
style: TextStyle(
color: AppTheme.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const Divider(),
_field(
controller: _oldCtrl,
label: '原密码',
hint: '请输入当前密码',
),
const SizedBox(height: 16),
_field(
controller: _newCtrl,
label: '新密码',
hint: '至少6位字符',
),
const SizedBox(height: 16),
_field(
controller: _confirmCtrl,
label: '确认新密码',
hint: '再次输入新密码',
),
if (_error != null) ...[
const SizedBox(height: 12),
Text(
_error!,
style: TextStyle(color: AppTheme.errorColor, fontSize: 12),
),
],
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton(
onPressed: _submitting
? null
: () {
_oldCtrl.clear();
_newCtrl.clear();
_confirmCtrl.clear();
setState(() => _error = null);
},
child: const Text('重置'),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: _submitting ? null : _submit,
icon: _submitting
? const SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Icon(Icons.check, size: 18),
label: const Text('确认'),
),
],
),
],
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
'默认密码为 123456',
style: TextStyle(color: AppTheme.textSecondary, fontSize: 12),
),
),
],
);
}
Widget _field({
required TextEditingController controller,
required String label,
required String hint,
}) {
return TextField(
controller: controller,
obscureText: true,
decoration: InputDecoration(
labelText: label,
hintText: hint,
border: const OutlineInputBorder(),
isDense: true,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
),
);
}
}