Skip to content

Commit

Permalink
Merge pull request #83 from fga-eps-mds/feat#59/profile_logout
Browse files Browse the repository at this point in the history
feat(\#59): implementa logout
  • Loading branch information
GabrielCostaDeOliveira authored Jan 24, 2025
2 parents 79cdb87 + f64722b commit 5437e55
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 113 deletions.
61 changes: 38 additions & 23 deletions lib/ui/profile/view/profile_view.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import 'package:flutter/material.dart';
import 'package:aranduapp/ui/edit_password/view/edit_password_view.dart';
import 'package:aranduapp/ui/login/view/login_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:aranduapp/ui/shared/profile_header.dart';
import 'package:aranduapp/ui/profile/viewmodel/profile_viewmodel.dart';
import 'package:aranduapp/ui/edit_profile/view/edit_profile_view.dart';
import 'package:aranduapp/ui/edit_password/view/edit_password_view.dart';

class Profile extends StatelessWidget {
const Profile({super.key});

@override
Widget build(BuildContext context) {
// Usando GetIt para acessar a instância da ProfileViewModel
final profileViewModel = GetIt.I<ProfileViewModel>();

return Scaffold(
appBar: _buildAppBar(context),
body: ChangeNotifierProvider.value(
value: profileViewModel,
value: GetIt.I<ProfileViewModel>(),
builder: (context, child) {
return _buildPage(context);
},
),
);
}

/// AppBar
AppBar _buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: Theme.of(context).colorScheme.onPrimary,
Expand All @@ -40,13 +37,11 @@ class Profile extends StatelessWidget {
actions: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Container(
child: Center(
child: Icon(
Icons.notifications_none_outlined,
color: Theme.of(context).colorScheme.primary,
size: 32,
),
child: Center(
child: Icon(
Icons.notifications_none_outlined,
color: Theme.of(context).colorScheme.primary,
size: 32,
),
),
),
Expand All @@ -64,6 +59,7 @@ class Profile extends StatelessWidget {
_buildProfileHeader(context),
const SizedBox(height: 80),
_setting(context),
const SizedBox(height: 80),
],
),
),
Expand All @@ -75,7 +71,7 @@ class Profile extends StatelessWidget {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ProfileHeader(
const ProfileHeader(
name: "Stefani",
role: "Estudante",
),
Expand Down Expand Up @@ -106,6 +102,8 @@ class Profile extends StatelessWidget {
}

Widget _setting(BuildContext context) {
ProfileViewModel viewModel = Provider.of<ProfileViewModel>(context);

return Card(
child: Column(
children: [
Expand All @@ -130,14 +128,31 @@ class Profile extends StatelessWidget {
},
),
const Divider(),
ListTile(
leading: Icon(
Icons.logout_sharp,
color: Theme.of(context).colorScheme.error,
size: 32,
),
title: const Text('Sair'),
onTap: () {},
ListenableBuilder(
listenable: viewModel.logoutCommand,
builder: (context, child) {
if (viewModel.logoutCommand.isOk) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const Login(),
),
);
});
}

return ListTile(
key: const Key('logout_button'),

leading: Icon(
Icons.logout_sharp,
color: Theme.of(context).colorScheme.error,
size: 32,
),
title: const Text('Sair'),
onTap: viewModel.logoutCommand.execute,
);
},
),
],
),
Expand Down
58 changes: 11 additions & 47 deletions lib/ui/profile/viewmodel/profile_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,56 +1,20 @@
import 'package:aranduapp/core/log/log.dart';
import 'package:aranduapp/core/state/command.dart';
import 'package:async/async.dart';
import 'package:flutter/material.dart';
import 'package:aranduapp/core/data/local/storage_value.dart';

class ProfileViewModel extends ChangeNotifier {
// Controllers e Key para o formulário
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController emailController = TextEditingController();
late Command0<void> logoutCommand;

// Estado de carregamento
bool _isLoading = false;
bool get isLoading => _isLoading;

// ProfileViewModel não precisa mais do BuildContext
ProfileViewModel();

// Método para simular o envio do e-mail de recuperação de senha
Future<void> forgetPassword() async {
// Verifica se o formulário é válido
if (!formKey.currentState!.validate()) {
return;
}

_setLoading(true);

try {
// Simulação de chamada de API
await Future.delayed(const Duration(seconds: 2));

// Validações ou chamadas reais para a API iriam aqui
final email = emailController.text.trim();
if (email.isEmpty) {
throw Exception("O campo de e-mail não pode estar vazio.");
}

// Log fictício para simular sucesso
debugPrint("E-mail de recuperação enviado para: $email");
} catch (e) {
// Relança a exceção para que o consumidor exiba o erro
throw Exception("Erro ao enviar o e-mail: $e");
} finally {
_setLoading(false);
}
ProfileViewModel() {
logoutCommand = Command0<void>(logout);
}

// Define o estado de carregamento e notifica os ouvintes
void _setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
Future<Result<void>> logout() async {
await StorageValue.getInstance().clear();

// Destruir controllers ao finalizar a ViewModel
@override
void dispose() {
emailController.dispose();
super.dispose();
Log.d("Usuário deslogado com sucesso.");
return Result.value(null);
}
}
48 changes: 38 additions & 10 deletions test/ui/profile/view/profile_view_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'package:aranduapp/core/state/command.dart';
import 'package:aranduapp/ui/login/view/login_view.dart';
import 'package:aranduapp/ui/login/viewmodel/login_viewmodel.dart';
import 'package:aranduapp/ui/profile/view/profile_view.dart';
import 'package:aranduapp/ui/profile/viewmodel/profile_viewmodel.dart';
import 'package:aranduapp/ui/shared/profile_header.dart';
Expand All @@ -9,22 +12,26 @@ import 'package:mockito/mockito.dart';

import 'profile_view_test.mocks.dart';

@GenerateNiceMocks([MockSpec<ProfileViewModel>()])
@GenerateNiceMocks([
MockSpec<ProfileViewModel>(),
MockSpec<Command0>(),
])
void main() {
late MockProfileViewModel mockProfileViewModel;
late MockCommand0 mocklogoutCommand0;

setUp(() {
setUp(() async {
mockProfileViewModel = MockProfileViewModel();
mocklogoutCommand0 = MockCommand0();

// Define os comportamentos padrão para o mock
when(mockProfileViewModel.isLoading)
.thenReturn(false); // Simula que o carregamento não está acontecendo
when(mockProfileViewModel.formKey).thenReturn(GlobalKey<FormState>());
when(mockProfileViewModel.emailController)
.thenReturn(TextEditingController());
when(mockProfileViewModel.logoutCommand).thenReturn(mocklogoutCommand0);
when(mocklogoutCommand0.running).thenReturn(false);
when(mocklogoutCommand0.isOk).thenReturn(false);
when(mocklogoutCommand0.running).thenReturn(false);

// Registra o mock como a instância do ViewModel usada pelo GetIt
await GetIt.instance.reset();
GetIt.I.registerSingleton<ProfileViewModel>(mockProfileViewModel);
GetIt.I.registerLazySingleton<LoginViewModel>(() => LoginViewModel());
});
Widget createScreen() {
return const MaterialApp(
Expand All @@ -33,7 +40,6 @@ void main() {
}

testWidgets('Profile screen displays', (WidgetTester tester) async {
// Carrega a tela
await tester.pumpWidget(createScreen());

expect(find.text('Perfil'), findsOneWidget);
Expand All @@ -43,4 +49,26 @@ void main() {
expect(find.text('Trocar senha'), findsOneWidget);
expect(find.text('Sair'), findsOneWidget);
});

testWidgets('tap logout', (WidgetTester tester) async {
await tester.pumpWidget(createScreen());

await tester.pumpAndSettle();

await tester.tap(find.byKey(const Key('logout_button')));

await tester.pumpAndSettle();

verify(mocklogoutCommand0.execute()).called(1);
});

testWidgets('logout', (WidgetTester tester) async {
when(mocklogoutCommand0.isOk).thenReturn(true);

await tester.pumpWidget(createScreen());

await tester.pumpAndSettle();

expect(find.byType(Login), findsOneWidget);
});
}
Loading

0 comments on commit 5437e55

Please sign in to comment.