create project

This commit is contained in:
2026-05-19 11:46:42 +08:00
commit dcdf1f60b1
60 changed files with 9309 additions and 0 deletions

View File

@@ -0,0 +1,435 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../providers/auth_provider.dart';
import '../../providers/family_provider.dart';
import '../../theme/colors.dart';
import '../../widgets/glass_card.dart';
class ProfileScreen extends ConsumerWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
final isLoggedIn = authState.isLoggedIn;
final user = authState.user;
return Scaffold(
backgroundColor: AppColors.pageBg,
body: isLoggedIn && user != null
? _LoggedInContent(user: user)
: const _LoggedOutContent(),
);
}
}
class _LoggedOutContent extends StatelessWidget {
const _LoggedOutContent();
@override
Widget build(BuildContext context) {
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
const Spacer(),
// Glass card with login prompt
GlassCard(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.brand, AppColors.brandLight],
),
shape: BoxShape.circle,
),
child: const Icon(
Icons.person_outline,
size: 40,
color: Colors.white,
),
),
const SizedBox(height: 20),
const Text(
'登录后可同步数据',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'管理家庭账本',
style: TextStyle(
fontSize: 14,
color: AppColors.textSecondary.withOpacity(0.8),
),
),
const SizedBox(height: 24),
// Login button
GestureDetector(
onTap: () {
Navigator.pushNamed(context, '/login');
},
child: Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.brand, AppColors.brandLight],
),
borderRadius: BorderRadius.circular(25),
),
child: const Center(
child: Text(
'立即登录',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
),
],
),
),
const Spacer(flex: 2),
],
),
),
);
}
}
class _LoggedInContent extends ConsumerWidget {
final dynamic user;
const _LoggedInContent({required this.user});
@override
Widget build(BuildContext context, WidgetRef ref) {
final familiesAsync = ref.watch(familyProvider);
final nickname = user.nickname ?? '用户';
final firstLetter = nickname.isNotEmpty ? nickname[0].toUpperCase() : 'U';
return SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: [
// User card with orange gradient avatar
GlassCard(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
children: [
// Orange gradient circular avatar with first letter
Container(
width: 64,
height: 64,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.brand, AppColors.brandLight],
),
shape: BoxShape.circle,
),
child: Center(
child: Text(
firstLetter,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
nickname,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 4),
Text(
'ID: ${user.id ?? ""}',
style: TextStyle(
fontSize: 13,
color: AppColors.textSecondary.withOpacity(0.8),
),
),
],
),
),
],
),
const SizedBox(height: 16),
// Email status and family info
Row(
children: [
Icon(
user.email != null && user.email!.isNotEmpty
? Icons.check_circle
: Icons.warning_amber_outlined,
size: 16,
color: user.email != null && user.email!.isNotEmpty
? AppColors.income
: AppColors.expense,
),
const SizedBox(width: 6),
Text(
user.email != null && user.email!.isNotEmpty
? '邮箱已绑定'
: '未绑定邮箱',
style: TextStyle(
fontSize: 13,
color: AppColors.textSecondary.withOpacity(0.8),
),
),
const Spacer(),
familiesAsync.when(
loading: () => const SizedBox.shrink(),
error: (_, __) => const SizedBox.shrink(),
data: (families) {
if (families.isEmpty) {
return const SizedBox.shrink();
}
return Row(
children: [
const Icon(
Icons.home_outlined,
size: 16,
color: AppColors.brand,
),
const SizedBox(width: 6),
Text(
families.first.name,
style: const TextStyle(
fontSize: 13,
color: AppColors.brand,
),
),
],
);
},
),
],
),
],
),
),
const SizedBox(height: 24),
// Settings groups with glassmorphism
_SettingsGroup(
title: '账本管理',
items: const [
_SettingsItem(icon: '🏠', label: '我的家庭', route: '/family'),
_SettingsItem(icon: '📒', label: '账本管理', route: '/books'),
_SettingsItem(icon: '💰', label: '预算设置', route: '/budget'),
_SettingsItem(icon: '🏷️', label: '分类管理', route: '/categories'),
],
),
const SizedBox(height: 16),
_SettingsGroup(
title: '系统设置',
items: const [
_SettingsItem(icon: '🤖', label: 'AI设置', route: '/ai-settings'),
_SettingsItem(icon: '📤', label: '数据导出', route: '/export'),
_SettingsItem(icon: '🔔', label: '提醒设置', route: '/notifications'),
],
),
const SizedBox(height: 16),
_SettingsGroup(
title: '关于',
items: const [
_SettingsItem(icon: '', label: '关于', route: '/about'),
_SettingsItem(icon: '💬', label: '帮助与反馈', route: '/feedback'),
],
),
const SizedBox(height: 24),
// Logout button with red border
GestureDetector(
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('确认退出'),
content: const Text('确定要退出登录吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text(
'退出',
style: TextStyle(color: AppColors.expense),
),
),
],
),
);
if (confirmed == true) {
await ref.read(authProvider.notifier).logout();
}
},
child: Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(25),
border: Border.all(color: AppColors.expense, width: 1.5),
),
child: const Center(
child: Text(
'退出登录',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.expense,
),
),
),
),
),
const SizedBox(height: 20),
],
),
),
);
}
}
class _SettingsGroup extends StatelessWidget {
final String title;
final List<_SettingsItem> items;
const _SettingsGroup({
required this.title,
required this.items,
});
@override
Widget build(BuildContext context) {
return GlassCard(
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.brand,
),
),
),
...items.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
final isLast = index == items.length - 1;
return Column(
children: [
_SettingsTile(item: item),
if (!isLast)
Divider(
height: 1,
indent: 56,
endIndent: 16,
color: AppColors.textSecondary.withOpacity(0.1),
),
],
);
}),
const SizedBox(height: 8),
],
),
);
}
}
class _SettingsItem {
final String icon;
final String label;
final String route;
const _SettingsItem({
required this.icon,
required this.label,
required this.route,
});
}
class _SettingsTile extends StatelessWidget {
final _SettingsItem item;
const _SettingsTile({required this.item});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.pushNamed(context, item.route);
},
behavior: HitTestBehavior.opaque,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Text(
item.icon,
style: const TextStyle(fontSize: 20),
),
const SizedBox(width: 12),
Expanded(
child: Text(
item.label,
style: const TextStyle(
fontSize: 15,
color: AppColors.textPrimary,
),
),
),
Icon(
Icons.chevron_right,
size: 20,
color: AppColors.textSecondary.withOpacity(0.5),
),
],
),
),
);
}
}