| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import '../../../core/l10n/app_localizations.dart';
- import '../../../core/theme/app_colors.dart';
- import '../../../providers/auth_provider.dart';
- // ── 输入方式 Tab(邮箱 / 手机号)────────────────────────────
- class AuthInputMethodTab extends StatelessWidget {
- const AuthInputMethodTab({
- super.key,
- required this.selected,
- required this.onSelect,
- });
- final AuthInputMethod selected;
- final ValueChanged<AuthInputMethod> onSelect;
- @override
- Widget build(BuildContext context) {
- return Row(
- children: [
- AuthTabItem(
- label: AppLocalizations.of(context)!.emailTab,
- active: selected == AuthInputMethod.email,
- onTap: () => onSelect(AuthInputMethod.email),
- ),
- const SizedBox(width: 24),
- AuthTabItem(
- label: AppLocalizations.of(context)!.phoneTab,
- active: selected == AuthInputMethod.phone,
- onTap: () => onSelect(AuthInputMethod.phone),
- ),
- ],
- );
- }
- }
- // ── 登录模式 Tab(密码 / 验证码)────────────────────────────
- class LoginModeTab extends StatelessWidget {
- const LoginModeTab({
- super.key,
- required this.selected,
- required this.onSelect,
- });
- final LoginMode selected;
- final ValueChanged<LoginMode> onSelect;
- @override
- Widget build(BuildContext context) {
- return Row(
- children: [
- AuthTabItem(
- label: AppLocalizations.of(context)!.passwordLoginTab,
- active: selected == LoginMode.password,
- onTap: () => onSelect(LoginMode.password),
- ),
- const SizedBox(width: 24),
- AuthTabItem(
- label: AppLocalizations.of(context)!.codeLoginTab,
- active: selected == LoginMode.code,
- onTap: () => onSelect(LoginMode.code),
- ),
- ],
- );
- }
- }
- // ── 单个 Tab 项 ───────────────────────────────────────────────
- class AuthTabItem extends StatelessWidget {
- const AuthTabItem({
- super.key,
- required this.label,
- required this.active,
- required this.onTap,
- });
- final String label;
- final bool active;
- final VoidCallback onTap;
- @override
- Widget build(BuildContext context) {
- final cs = Theme.of(context).colorScheme;
- return GestureDetector(
- onTap: onTap,
- child: Column(
- children: [
- Text(
- label,
- style: TextStyle(
- color: active ? cs.onSurface : cs.onSurface.withAlpha(153),
- fontSize: 15,
- fontWeight: active ? FontWeight.w600 : FontWeight.w400,
- ),
- ),
- const SizedBox(height: 4),
- if (active)
- Container(
- height: 2,
- width: 24,
- decoration: BoxDecoration(
- color: AppColors.brand,
- borderRadius: BorderRadius.circular(1),
- ),
- ),
- ],
- ),
- );
- }
- }
- // ── 通用输入框 ────────────────────────────────────────────────
- class AuthField extends StatelessWidget {
- const AuthField({
- super.key,
- required this.controller,
- required this.hint,
- this.obscure = false,
- this.suffixIcon,
- this.keyboardType,
- this.onChanged,
- this.maxLength,
- this.semanticsLabel,
- this.focusNode,
- this.errorText,
- this.inputFormatters,
- });
- final TextEditingController controller;
- final String hint;
- final bool obscure;
- final Widget? suffixIcon;
- final TextInputType? keyboardType;
- final ValueChanged<String>? onChanged;
- final int? maxLength;
- final String? semanticsLabel;
- final FocusNode? focusNode;
- final String? errorText;
- final List<TextInputFormatter>? inputFormatters;
- @override
- Widget build(BuildContext context) {
- final cs = Theme.of(context).colorScheme;
- final hasError = errorText != null && errorText!.isNotEmpty;
- final errorBorder = OutlineInputBorder(
- borderRadius: BorderRadius.circular(10),
- borderSide: BorderSide(color: cs.error, width: 1.5),
- );
- return Semantics(
- label: semanticsLabel,
- textField: true,
- child: TextField(
- controller: controller,
- focusNode: focusNode,
- obscureText: obscure,
- keyboardType: keyboardType,
- onChanged: onChanged,
- maxLength: maxLength,
- inputFormatters: inputFormatters,
- buildCounter: maxLength != null
- ? (_, {required currentLength, required isFocused, required maxLength}) => null
- : null,
- style: TextStyle(color: cs.onSurface, fontSize: 15),
- decoration: InputDecoration(
- hintText: hint,
- hintStyle: TextStyle(color: cs.onSurface.withAlpha(80), fontSize: 15),
- contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
- suffixIcon: suffixIcon,
- errorText: errorText,
- errorStyle: TextStyle(color: cs.error, fontSize: 12),
- // 聚焦态:品牌黄描边,覆盖全局主题的主文字色
- focusedBorder: hasError
- ? errorBorder
- : OutlineInputBorder(
- borderRadius: BorderRadius.circular(10),
- borderSide: const BorderSide(color: AppColors.brand, width: 1.5),
- ),
- enabledBorder: hasError ? errorBorder : null,
- ),
- ),
- );
- }
- }
- // ── 发送验证码按钮 ────────────────────────────────────────────
- class SendCodeButton extends StatelessWidget {
- const SendCodeButton({
- super.key,
- required this.countdown,
- required this.onTap,
- });
- final int countdown;
- final VoidCallback? onTap;
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- onTap: onTap,
- child: Container(
- height: 50,
- padding: const EdgeInsets.symmetric(horizontal: 14),
- decoration: BoxDecoration(
- color: onTap != null ? AppColors.brand : AppColors.brand.withAlpha(60),
- borderRadius: BorderRadius.circular(10),
- ),
- child: Center(
- child: Text(
- countdown > 0 ? '${countdown}s' : AppLocalizations.of(context)!.sendCode,
- style: TextStyle(
- color: onTap != null ? Colors.black : Colors.black.withAlpha(100),
- fontSize: 13,
- fontWeight: FontWeight.w600,
- ),
- ),
- ),
- ),
- );
- }
- }
|