diff --git a/client/lib/widgets/auth_screen.dart b/client/lib/widgets/auth_screen.dart index b48c830..8c26cb1 100644 --- a/client/lib/widgets/auth_screen.dart +++ b/client/lib/widgets/auth_screen.dart @@ -128,6 +128,33 @@ class _AuthScreenState extends ConsumerState } } + // ── Enter 键导航 / 提交 ─────────────────────────────────────────── + // 登录:邮箱回车 → 跳密码;密码回车 → 直接登录(校验条件同主按钮 enabled)。 + void _submitLogin() { + if (!_loading && _emailValid && _pw.text.isNotEmpty) _doLogin(); + } + + // 注册:邮箱回车 → 发码并跳验证码;验证码(6位)回车 → 进设密码步并聚焦密码; + // 密码(≥6位)回车 → 直接注册。 + void _onRegisterEmailSubmit() { + if (_loading || !_emailValid) return; + if (!_sent) _sendCode(); + _codeFocus.requestFocus(); + } + + void _onRegisterCodeSubmit() { + if (!_sent || _code.text.length != 6) return; + setState(() => _step = 1); + // step 1 的密码框在本帧之后才挂载,延到下一帧再聚焦。 + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) _pwFocus.requestFocus(); + }); + } + + void _submitRegister() { + if (!_loading && _pw.text.length >= 6) _doRegister(); + } + void _setMode(_AuthMode m) => setState(() { _mode = m; _step = 0; @@ -335,12 +362,14 @@ class _AuthScreenState extends ConsumerState kBareInput.copyWith(contentPadding: const EdgeInsets.symmetric(vertical: 13)); // 密码输入 + 显示/隐藏切换(眼睛按钮)。 - Widget _pwInput(PangolinScheme c, String hint) => Row(children: [ + Widget _pwInput(PangolinScheme c, String hint, {VoidCallback? onSubmit}) => Row(children: [ Expanded( child: TextField( controller: _pw, focusNode: _pwFocus, obscureText: !_pwVisible, + textInputAction: TextInputAction.done, + onSubmitted: onSubmit == null ? null : (_) => onSubmit(), decoration: _bare.copyWith(hintText: hint), onChanged: (_) => setState(() {}), ), @@ -370,9 +399,12 @@ class _AuthScreenState extends ConsumerState Widget _login(PangolinScheme c, AppText t) { return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _field(c, icon: PangolinIcons.mail, label: t.emailLabel, focused: _emailFocus.hasFocus, - child: TextField(controller: _email, focusNode: _emailFocus, decoration: _bare.copyWith(hintText: t.emailPh), onChanged: (_) => setState(() {}))), + child: TextField(controller: _email, focusNode: _emailFocus, + textInputAction: TextInputAction.next, + onSubmitted: (_) => _pwFocus.requestFocus(), + decoration: _bare.copyWith(hintText: t.emailPh), onChanged: (_) => setState(() {}))), const SizedBox(height: 16), - _field(c, icon: PangolinIcons.lock, label: t.pwLabel, focused: _pwFocus.hasFocus, child: _pwInput(c, t.pwPh)), + _field(c, icon: PangolinIcons.lock, label: t.pwLabel, focused: _pwFocus.hasFocus, child: _pwInput(c, t.pwPh, onSubmit: _submitLogin)), Align( alignment: Alignment.centerRight, child: Padding( @@ -391,7 +423,10 @@ class _AuthScreenState extends ConsumerState return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _field(c, icon: PangolinIcons.mail, label: t.emailLabel, focused: _emailFocus.hasFocus, child: Row(children: [ - Expanded(child: TextField(controller: _email, focusNode: _emailFocus, decoration: _bare.copyWith(hintText: t.emailPh), onChanged: (_) => setState(() {}))), + Expanded(child: TextField(controller: _email, focusNode: _emailFocus, + textInputAction: TextInputAction.next, + onSubmitted: (_) => _onRegisterEmailSubmit(), + decoration: _bare.copyWith(hintText: t.emailPh), onChanged: (_) => setState(() {}))), GestureDetector( onTap: (!_loading && _emailValid) ? _sendCode : null, child: Text(_sent ? t.resend : t.sendCode, @@ -410,6 +445,8 @@ class _AuthScreenState extends ConsumerState const SizedBox(height: 16), _field(c, icon: PangolinIcons.shieldCheck, label: t.codeLabel, focused: _codeFocus.hasFocus, child: TextField(controller: _code, focusNode: _codeFocus, keyboardType: TextInputType.number, maxLength: 6, + textInputAction: TextInputAction.next, + onSubmitted: (_) => _onRegisterCodeSubmit(), style: PangolinText.mono.copyWith(letterSpacing: 6, color: c.fg1), decoration: _bare.copyWith(counterText: '', hintText: ''), onChanged: (_) => setState(() {}))), const SizedBox(height: 16), @@ -434,7 +471,7 @@ class _AuthScreenState extends ConsumerState ]), ), const SizedBox(height: 16), - _field(c, icon: PangolinIcons.lock, label: t.pwLabel, focused: _pwFocus.hasFocus, child: _pwInput(c, t.setPwPh)), + _field(c, icon: PangolinIcons.lock, label: t.pwLabel, focused: _pwFocus.hasFocus, child: _pwInput(c, t.setPwPh, onSubmit: _submitRegister)), const SizedBox(height: 18), _primaryBtn(c, label: _loading ? '...' : t.doCreate, onPressed: (!_loading && _pw.text.length >= 6) ? _doRegister : null),