From 8da9c4152c3cfd30d72664eabbe95b28691d3137 Mon Sep 17 00:00:00 2001 From: virtus Date: Mon, 23 Mar 2026 16:58:53 +0700 Subject: [PATCH] feat: implement profile screen with user info, stats, and logout --- .../profile/presentation/profile_screen.dart | 146 ++++++++++++++++-- 1 file changed, 134 insertions(+), 12 deletions(-) diff --git a/lib/features/profile/presentation/profile_screen.dart b/lib/features/profile/presentation/profile_screen.dart index d52e9ed..b5257a6 100644 --- a/lib/features/profile/presentation/profile_screen.dart +++ b/lib/features/profile/presentation/profile_screen.dart @@ -1,24 +1,146 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../app/router/route_names.dart'; -import '../../../shared/widgets/feature_placeholder.dart'; +import '../../auth/providers/auth_provider.dart'; +import '../../bookshelf/providers/bookshelf_provider.dart'; -class ProfileScreen extends StatelessWidget { +class ProfileScreen extends ConsumerWidget { const ProfileScreen({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final authState = ref.watch(authProvider); + final bookshelfAsync = ref.watch(bookshelfProvider); + return Scaffold( - appBar: AppBar(title: const Text('Tai khoan')), - body: FeaturePlaceholder( - title: 'User Profile', - description: - 'Khung profile user, thong tin session, thong ke bookmark/de cu va cac cai dat doc dong bo.', - actions: [ - FilledButton( - onPressed: () => context.push(RouteNames.settings), - child: const Text('Mo cai dat doc'), + appBar: AppBar(title: const Text('Tài khoản')), + body: authState.maybeWhen( + authenticated: (user) => SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + // User Avatar & Basic Info + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + CircleAvatar( + radius: 40, + backgroundImage: user.image != null + ? NetworkImage(user.image!) + : null, + child: user.image == null + ? Text( + user.name.isNotEmpty + ? user.name[0].toUpperCase() + : '?', + style: Theme.of(context).textTheme.headlineMedium, + ) + : null, + ), + const SizedBox(height: 12), + Text( + user.name, + style: Theme.of(context).textTheme.titleLarge, + textAlign: TextAlign.center, + ), + const SizedBox(height: 4), + Text( + user.email, + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + const SizedBox(height: 24), + + // Stats Cards + Row( + children: [ + Expanded( + child: _buildStatCard( + context: context, + label: 'Sách Đánh Dấu', + count: bookshelfAsync.whenData((b) => b.length).value ?? 0, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildStatCard( + context: context, + label: 'Đang Đọc', + count: bookshelfAsync + .whenData((b) => b.where((x) => true).length) + .value ?? + 0, + ), + ), + ], + ), + const SizedBox(height: 24), + + // Settings Button + SizedBox( + width: double.infinity, + child: FilledButton.icon( + onPressed: () => context.push(RouteNames.settings), + icon: const Icon(Icons.tune), + label: const Text('Cài Đặt Đọc'), + ), + ), + const SizedBox(height: 12), + + // Logout Button + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: () async { + await ref.read(authProvider.notifier).signOut(); + if (context.mounted) context.go(RouteNames.login); + }, + icon: const Icon(Icons.logout), + label: const Text('Đăng Xuất'), + ), + ), + ], + ), + ), + orElse: () => const Center(child: CircularProgressIndicator()), + ), + ); + } + + Widget _buildStatCard({ + required BuildContext context, + required String label, + required int count, + }) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + Text( + count.toString(), + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 4), + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + textAlign: TextAlign.center, ), ], ),