two_factor_screen.dart 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import 'package:flutter/material.dart';
  2. import 'package:go_router/go_router.dart';
  3. import '../../../core/l10n/app_localizations.dart';
  4. import '../../../core/theme/app_colors.dart';
  5. class TwoFactorScreen extends StatefulWidget {
  6. const TwoFactorScreen({super.key});
  7. @override
  8. State<TwoFactorScreen> createState() => _TwoFactorScreenState();
  9. }
  10. class _TwoFactorScreenState extends State<TwoFactorScreen> {
  11. final List<TextEditingController> _controllers =
  12. List.generate(6, (_) => TextEditingController());
  13. final List<FocusNode> _focusNodes = List.generate(6, (_) => FocusNode());
  14. @override
  15. void dispose() {
  16. for (final c in _controllers) { c.dispose(); }
  17. for (final f in _focusNodes) { f.dispose(); }
  18. super.dispose();
  19. }
  20. void _onDigitChanged(int index, String value) {
  21. if (value.isNotEmpty && index < 5) {
  22. _focusNodes[index + 1].requestFocus();
  23. }
  24. if (value.isEmpty && index > 0) {
  25. _focusNodes[index - 1].requestFocus();
  26. }
  27. setState(() {});
  28. }
  29. bool get _allFilled => _controllers.every((c) => c.text.isNotEmpty);
  30. @override
  31. Widget build(BuildContext context) {
  32. final cs = Theme.of(context).colorScheme;
  33. return Scaffold(
  34. appBar: AppBar(
  35. elevation: 0,
  36. leading: IconButton(
  37. icon: const Icon(Icons.chevron_left, size: 28),
  38. onPressed: () => context.pop(),
  39. ),
  40. title: Text(
  41. AppLocalizations.of(context)!.twoFactorVerification,
  42. style: const TextStyle(
  43. fontSize: 18,
  44. fontWeight: FontWeight.w600,
  45. ),
  46. ),
  47. centerTitle: true,
  48. ),
  49. body: Padding(
  50. padding: const EdgeInsets.fromLTRB(24, 32, 24, 32),
  51. child: Column(
  52. crossAxisAlignment: CrossAxisAlignment.start,
  53. children: [
  54. // ── 说明 ──────────────────────────────────────
  55. Text(
  56. AppLocalizations.of(context)!.googleCode,
  57. style: TextStyle(
  58. color: cs.onSurface,
  59. fontSize: 22,
  60. fontWeight: FontWeight.w700,
  61. ),
  62. ),
  63. const SizedBox(height: 8),
  64. Text(
  65. AppLocalizations.of(context)!.googleAuthCodeHint,
  66. style: TextStyle(
  67. color: cs.onSurface.withAlpha(153),
  68. fontSize: 14,
  69. height: 1.5,
  70. ),
  71. ),
  72. const SizedBox(height: 36),
  73. // ── 6 位数字输入格 ─────────────────────────────
  74. Row(
  75. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  76. children: List.generate(6, (i) {
  77. return SizedBox(
  78. width: 48,
  79. height: 56,
  80. child: TextField(
  81. controller: _controllers[i],
  82. focusNode: _focusNodes[i],
  83. textAlign: TextAlign.center,
  84. keyboardType: TextInputType.number,
  85. maxLength: 1,
  86. onChanged: (v) => _onDigitChanged(i, v),
  87. style: TextStyle(
  88. color: cs.onSurface,
  89. fontSize: 22,
  90. fontWeight: FontWeight.w600,
  91. ),
  92. decoration: InputDecoration(
  93. counterText: '',
  94. filled: true,
  95. fillColor: cs.surface,
  96. border: OutlineInputBorder(
  97. borderRadius: BorderRadius.circular(10),
  98. borderSide: BorderSide.none,
  99. ),
  100. focusedBorder: OutlineInputBorder(
  101. borderRadius: BorderRadius.circular(10),
  102. borderSide: const BorderSide(color: AppColors.brand, width: 1.5),
  103. ),
  104. ),
  105. ),
  106. );
  107. }),
  108. ),
  109. const SizedBox(height: 36),
  110. // ── 确认按钮 ──────────────────────────────────
  111. SizedBox(
  112. width: double.infinity,
  113. height: 50,
  114. child: ElevatedButton(
  115. onPressed: _allFilled
  116. ? () {
  117. // Mock: verify and go home
  118. context.go('/');
  119. }
  120. : null,
  121. style: ElevatedButton.styleFrom(
  122. backgroundColor: AppColors.brand,
  123. disabledBackgroundColor: AppColors.brand.withAlpha(80),
  124. shape: RoundedRectangleBorder(
  125. borderRadius: BorderRadius.circular(10),
  126. ),
  127. ),
  128. child: Text(
  129. AppLocalizations.of(context)!.confirm,
  130. style: TextStyle(
  131. color: _allFilled ? cs.surface : cs.surface.withAlpha(153),
  132. fontSize: 16,
  133. fontWeight: FontWeight.w600,
  134. ),
  135. ),
  136. ),
  137. ),
  138. const SizedBox(height: 20),
  139. // ── 找不到验证码 ──────────────────────────────
  140. Center(
  141. child: GestureDetector(
  142. onTap: () {},
  143. child: Text(
  144. AppLocalizations.of(context)!.cantUseGoogleAuth,
  145. style: const TextStyle(
  146. color: AppColors.brand,
  147. fontSize: 14,
  148. ),
  149. ),
  150. ),
  151. ),
  152. ],
  153. ),
  154. ),
  155. );
  156. }
  157. }