feat(apps): 重构 UI 架构为 presentation 层并新增 l10n 国际化支持

This commit is contained in:
qzl
2026-03-27 14:05:03 +08:00
parent b1f0eb8921
commit c592cc7854
178 changed files with 10748 additions and 5764 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:social_app/core/api/i_api_client.dart';
import 'package:social_app/core/network/i_api_client.dart';
import 'models/signup_request.dart';
import 'models/login_request.dart';
import 'models/auth_response.dart';
@@ -3,10 +3,11 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:equatable/equatable.dart';
import '../../../../core/api/api_exception.dart';
import '../../../../core/network/api_exception.dart';
import '../../../../core/l10n/l10n.dart';
import '../../data/auth_repository.dart';
import '../../data/models/auth_response.dart';
import '../../../../core/form_inputs/form_inputs.dart';
import '../../../../shared/forms/inputs.dart';
class LoginState extends Equatable {
static const defaultDialCode = '+86';
@@ -121,7 +122,7 @@ class LoginCubit extends Cubit<LoginState> {
Future<bool> sendCode() async {
if (!state.phone.isValid) {
emit(state.copyWith(errorMessage: '请输入有效手机号'));
emit(state.copyWith(errorMessage: L10n.current.authInvalidPhone));
return false;
}
if (!state.canSendCode) {
@@ -152,7 +153,9 @@ class LoginCubit extends Cubit<LoginState> {
if (isClosed) {
return false;
}
final message = e is ApiException ? e.message : '验证码发送失败';
final message = e is ApiException
? e.message
: L10n.current.authSendCodeFailed;
emit(state.copyWith(isSendingCode: false, errorMessage: message));
return false;
}
@@ -4,8 +4,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/di/injection.dart';
import '../../../../core/router/app_routes.dart';
import '../../../../app/di/injection.dart';
import '../../../../app/router/app_routes.dart';
import '../../../../core/l10n/l10n.dart';
import '../../../../core/theme/design_tokens.dart';
import '../../../../shared/widgets/app_button.dart';
import '../../../../shared/widgets/banner/app_banner.dart';
@@ -85,12 +86,13 @@ class _LoginViewState extends State<LoginView> {
}
Future<bool> _showAgreementDialog() async {
final l10n = context.l10n;
return await showConfirmSheet(
context,
title: '请先同意协议',
message: '在使用我们的服务之前,请先阅读并同意《用户协议》和《隐私政策》。\n\n只有您同意上述协议,我们才能为您提供服务。',
confirmText: '确认',
cancelText: '取消',
title: l10n.authAgreementTitle,
message: l10n.authAgreementMessage,
confirmText: l10n.commonConfirm,
cancelText: l10n.commonCancel,
);
}
@@ -101,7 +103,7 @@ class _LoginViewState extends State<LoginView> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Semantics(
label: '同意用户协议与隐私政策',
label: context.l10n.authAgreementSemantics,
checked: _agreedToTerms,
button: true,
child: InkWell(
@@ -143,17 +145,17 @@ class _LoginViewState extends State<LoginView> {
text: TextSpan(
style: const TextStyle(fontSize: 13, color: AppColors.slate600),
children: [
const TextSpan(text: '我已同意'),
TextSpan(text: context.l10n.authAgreementPrefix),
TextSpan(
text: '《用户协议》',
text: context.l10n.authAgreementTerms,
style: const TextStyle(
color: AppColors.blue600,
decoration: TextDecoration.underline,
),
),
const TextSpan(text: ''),
TextSpan(text: context.l10n.authAgreementAnd),
TextSpan(
text: '《隐私政策》',
text: context.l10n.authAgreementPrivacy,
style: const TextStyle(
color: AppColors.blue600,
decoration: TextDecoration.underline,
@@ -213,7 +215,7 @@ class _LoginViewState extends State<LoginView> {
CrossAxisAlignment.stretch,
children: [
AuthField(
hint: '输入手机号',
hint: context.l10n.authPhoneHint,
controller: _phoneController,
onChanged: (value) {
context.read<LoginCubit>().phoneChanged(
@@ -237,7 +239,7 @@ class _LoginViewState extends State<LoginView> {
),
SizedBox(height: AppSpacing.lg),
AuthField(
hint: '输入验证码',
hint: context.l10n.authCodeHint,
controller: _codeController,
onChanged: (value) {
context.read<LoginCubit>().codeChanged(
@@ -256,7 +258,7 @@ class _LoginViewState extends State<LoginView> {
child: LinkButton(
text: state.resendCooldownSeconds > 0
? '${state.resendCooldownSeconds}s'
: '发送验证码',
: context.l10n.authSendCode,
onTap: state.canSendCode
? _handleSendCode
: null,
@@ -276,8 +278,8 @@ class _LoginViewState extends State<LoginView> {
? ToastType.error
: ToastType.warning,
title: state.errorMessage != null
? '登录失败'
: '请检查输入',
? context.l10n.authLoginFailed
: context.l10n.authCheckInput,
),
),
],
@@ -285,7 +287,7 @@ class _LoginViewState extends State<LoginView> {
),
SizedBox(height: AppSpacing.xxl),
AppButton(
text: '登录/注册',
text: context.l10n.authLoginOrRegister,
onPressed:
state.status ==
FormzSubmissionStatus.inProgress
@@ -146,8 +146,8 @@ class AuthHeroHeader extends StatelessWidget {
borderRadius: BorderRadius.circular(AppRadius.full),
child: Image.asset(
'assets/images/logo.png',
width: 58,
height: 58,
width: 88,
height: 88,
fit: BoxFit.cover,
),
),
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../../../../core/l10n/l10n.dart';
import '../../../../core/theme/design_tokens.dart';
import 'auth_field.dart';
@@ -40,7 +41,9 @@ class _PasswordFieldState extends State<PasswordField> {
onChanged: widget.onChanged,
suffixIcon: IconButton(
onPressed: _toggleVisibility,
tooltip: _obscured ? '显示密码' : '隐藏密码',
tooltip: _obscured
? context.l10n.authShowPassword
: context.l10n.authHidePassword,
icon: Icon(
_obscured ? Icons.visibility_off_rounded : Icons.visibility_rounded,
color: AppColors.authInputIcon,