import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:go_router/go_router.dart'; import 'package:plan/api/endpoints/user_api.dart'; import 'package:plan/api/network/safe.dart'; import 'package:provider/provider.dart'; import '../../../providers/app_store.dart'; import '../../../router/config/route_paths.dart'; class LoginCodePage extends StatefulWidget { final String email; final String password; const LoginCodePage({super.key, required this.email, required this.password}); @override State createState() => _LoginCodePageState(); } class _LoginCodePageState extends State { final List _focusNodes = List.generate(4, (_) => FocusNode()); final List _controllers = List.generate( 4, (_) => TextEditingController(), ); //倒计时 int _count = 60; Timer? _timer; @override void initState() { super.initState(); _handSendCode(); _handClear(); } @override void dispose() { _timer?.cancel(); _handClear(); super.dispose(); } ///小输入框改变时 void _onChanged(String value, int index) async { //一键复制 if (value.length == 4) { _handlePaste(value); } //提交 if (value.isNotEmpty && index == 3) { _handSubmit(); return; } // 自动跳到下一格 if (value.length == 1 && index < 3) { _focusNodes[index + 1].requestFocus(); } } void _handlePaste(String pastedText) { // 只取前4位数字 final digits = pastedText.replaceAll(RegExp(r'[^0-9]'), ''); for (int i = 0; i < 4; i++) { _controllers[i].text = i < digits.length ? digits[i] : ''; } if (digits.length >= 4) { _focusNodes[3].requestFocus(); _handSubmit(); } else if (digits.isNotEmpty) { _focusNodes[digits.length].requestFocus(); } } ///删除键 void _onDelete(KeyEvent event, int index) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.backspace) { final currentController = _controllers[index]; if (currentController.text.isEmpty && index > 0) { _focusNodes[index - 1].requestFocus(); _controllers[index - 1].clear(); } } } ///发送验证码 void _handSendCode() { if (_count != 60) { return; } _timer = Timer.periodic(Duration(seconds: 1), (timer) { setState(() { _count--; }); if (_count == 0) { setState(() { _count = 60; }); timer.cancel(); } }); sendEmailCodeApi(widget.email); EasyLoading.showToast("Send success"); } ///提交 void _handSubmit() async { String code = _controllers.map((controller) => controller.text).join(); if (code.length == 4) { EasyLoading.show(); var res = await safeRequest( registerApi( widget.email, widget.password, code, ), onError: (error) { _handClear(); EasyLoading.showToast("Login Error"); }, ); var appStore = context.read(); await appStore.setInfo(res); context.go(RoutePaths.layout); } } ///清空 void _handClear() { for (var controller in _controllers) { controller.clear(); } _focusNodes.first.requestFocus(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar(), body: ListView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(20), children: [ Container( margin: EdgeInsets.only(bottom: 20), child: Text( "Check your inbox", style: Theme.of(context).textTheme.titleLarge, ), ), Container( margin: EdgeInsets.only(bottom: 60), child: Text( "Verification code has been sent to ${widget.email}", style: Theme.of(context).textTheme.labelLarge, ), ), Row( spacing: 20, children: List.generate(4, (index) { return Expanded( child: AspectRatio( aspectRatio: 1, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainer, borderRadius: BorderRadius.circular(10), ), child: KeyboardListener( focusNode: FocusNode(), onKeyEvent: (event) { _onDelete(event, index); }, child: TextField( controller: _controllers[index], focusNode: _focusNodes[index], textAlign: TextAlign.center, keyboardType: TextInputType.number, maxLength: 4, style: TextStyle(fontSize: 32), decoration: InputDecoration( counterText: "", border: InputBorder.none, isCollapsed: true, ), onChanged: (value) { _onChanged(value, index); }, ), ), ), ), ); }), ), Container( margin: EdgeInsets.only(top: 30), child: Row( children: [ GestureDetector( onTap: _handSendCode, child: Visibility( visible: _count != 60, replacement: Text( "Resend Code", style: Theme.of(context).textTheme.bodySmall, ), child: Text( "Resend Code(${_count}s)", style: Theme.of(context).textTheme.labelMedium, ), ), ), ], ), ), ], ), ); } }