Files
social-app/apps/lib/features/contacts/ui/screens/add_contact_screen.dart
T
qzl b34697660d feat: 实现 Auth 全局状态机与 401 统一处理机制
- 新增 AuthSessionInvalidated 事件处理 token 失效场景
- ApiInterceptor 新增 authFailureCallback 单飞机制
- AuthBloc 区分 manual logout 与 auto expiry 语义
- 新增 startup recovery fallback 防止启动卡死

feat: 重构 Calendar DayWeek 视图事件布局引擎

- 新增 DayEventLayoutEngine 解耦事件计算与渲染
- 新增 DayTimelineMetrics 统一时间轴常量
- 新增 DayViewScale 支持捏合缩放

feat: 新增 Settings 页面共享 UI 组件

- 新增 BackTitlePageHeader 统一页面 header
- 新增 DetailHeaderActionMenu 统一操作菜单
- 新增 DestructiveActionSheet 统一删除确认
- 新增 AppToggleSwitch 统一开关组件

feat: Chat UI Schema 支持导航操作

- 支持 navigation 类型 action 触发内部路由跳转
- 新增路径验证与参数处理

chore: 更新相关测试覆盖 auth 失效路径
2026-03-18 13:35:25 +08:00

190 lines
5.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/theme/design_tokens.dart';
import '../../../../shared/widgets/back_title_page_header.dart';
import '../../../../shared/widgets/app_input.dart';
import '../../../../shared/widgets/link_button.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
class AddContactScreen extends StatefulWidget {
final String? contactId;
const AddContactScreen({super.key, this.contactId});
@override
State<AddContactScreen> createState() => _AddContactScreenState();
}
class _AddContactScreenState extends State<AddContactScreen> {
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _remarkController = TextEditingController();
bool get isEditing => widget.contactId != null;
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_remarkController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.surfaceSecondary,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BackTitlePageHeader(
title: isEditing ? '编辑联系人' : '添加联系人',
onBack: () => context.pop(),
trailing: _buildConfirmButton(),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildAvatarSection(),
const SizedBox(height: 14),
_buildFormCard(),
if (isEditing) ...[
const SizedBox(height: 14),
_buildDeleteRow(),
],
],
),
),
),
],
),
),
);
}
Widget _buildConfirmButton() {
return SizedBox(
width: AppSpacing.xxl * 2,
height: AppSpacing.xxl * 2,
child: TextButton(
onPressed: _handleConfirm,
style: TextButton.styleFrom(
padding: const EdgeInsets.all(AppSpacing.none),
backgroundColor: AppColors.surfaceInfo,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.full),
side: const BorderSide(color: AppColors.borderQuaternary),
),
),
child: const Icon(
Icons.check,
size: AppSpacing.lg,
color: AppColors.blue600,
),
),
);
}
Widget _buildAvatarSection() {
return Center(
child: Container(
width: 72,
height: 72,
decoration: BoxDecoration(
color: AppColors.surfaceInfoLight,
borderRadius: BorderRadius.circular(36),
border: Border.all(color: Colors.transparent),
),
child: const Icon(
Icons.person_outline,
size: 24,
color: AppColors.slate400,
),
),
);
}
Widget _buildFormCard() {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.messageCardBorder),
),
child: Column(
children: [
AppInput(label: '昵称', hint: '请输入昵称', controller: _nameController),
const SizedBox(height: 14),
AppInput(
label: '邮箱',
hint: '请输入邮箱',
controller: _emailController,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 14),
AppInput(
label: '备注',
hint: '请输入备注',
controller: _remarkController,
maxLines: 3,
),
],
),
);
}
Widget _buildDeleteRow() {
return Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
child: LinkButton(
text: '删除联系人',
onTap: _handleDelete,
foregroundColor: AppColors.red600,
),
);
}
void _handleConfirm() {
final name = _nameController.text.trim();
final email = _emailController.text.trim();
if (name.isEmpty || email.isEmpty) {
Toast.show(context, '请填写昵称和邮箱', type: ToastType.warning);
return;
}
// TODO: Implement save logic
context.pop();
}
void _handleDelete() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('删除联系人'),
content: const Text('确定要删除此联系人吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
// TODO: Implement delete logic
context.pop();
},
child: const Text('删除', style: TextStyle(color: AppColors.red600)),
),
],
),
);
}
}