From bbc4d6a632dc1bf67c449b1fb66317ac5d08194a Mon Sep 17 00:00:00 2001 From: Syaroful Date: Wed, 16 Oct 2024 14:25:33 +0700 Subject: [PATCH] feat: add logout features --- .../lib/domain/service/app_service.dart | 30 +++ .../control/provider/control_provider.dart | 4 +- .../view/conductivity_screen.dart | 2 +- .../home/pages/ph/view/ph_screen.dart | 4 +- .../lib/features/home/view/home_screen.dart | 174 +++++++++--------- .../setting/provider/setting_provider.dart | 24 ++- .../features/setting/view/setting_screen.dart | 138 +++++++------- 7 files changed, 221 insertions(+), 155 deletions(-) diff --git a/agrilink_vocpro/lib/domain/service/app_service.dart b/agrilink_vocpro/lib/domain/service/app_service.dart index 1698517..b582605 100644 --- a/agrilink_vocpro/lib/domain/service/app_service.dart +++ b/agrilink_vocpro/lib/domain/service/app_service.dart @@ -64,6 +64,36 @@ class AppService { } } + // logout + Future logout() async { + final SharedPreferences pref = await SharedPreferences.getInstance(); + final String auth = 'Bearer ${pref.getString('token')}'; + + try { + final response = await _dioWithoutInterceptor.post( + '/auth/logout', + options: Options( + headers: {'Authorization': auth}, + ), + ); + if (response.statusCode == 200) { + final data = LoginResponse.fromJson(response.data); + pref.remove('token'); + pref.remove('jwtToken'); + pref.remove('username'); + pref.remove('email'); + pref.remove('fullName'); + pref.setBool('isLoggedIn', false); + return data; + } else { + throw Exception('Failed to load data'); + } + } on DioException catch (e) { + final String errorMessage = e.response?.data['message']; + throw (errorMessage); + } + } + Future getRelayStatus() async { final SharedPreferences pref = await SharedPreferences.getInstance(); final String auth = 'Bearer ${pref.getString('token')}'; diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart index ef570bf..378a2a3 100644 --- a/agrilink_vocpro/lib/features/control/provider/control_provider.dart +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -96,7 +96,9 @@ class ControlProvider extends ChangeNotifier { if (result.success == true) { relayState = ResultState.hasData; notifyListeners(); - print(result.message); + if (kDebugMode) { + print(result.message); + } return true; } else { return false; diff --git a/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart b/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart index 3653ca5..c54f0a0 100644 --- a/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart @@ -33,7 +33,7 @@ class ConductivityScreen extends StatelessWidget { padding: EdgeInsets.only(right: 16), child: Icon( Icons.electric_bolt_rounded, - color: Colors.green, + color: Colors.teal, ), ) ], diff --git a/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart b/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart index f6264de..7d2ab0e 100644 --- a/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart @@ -32,8 +32,8 @@ class PhScreen extends StatelessWidget { Padding( padding: EdgeInsets.only(right: 16), child: Icon( - BootstrapIcons.water, - color: Colors.green, + BootstrapIcons.pie_chart, + color: Colors.amber, ), ) ], diff --git a/agrilink_vocpro/lib/features/home/view/home_screen.dart b/agrilink_vocpro/lib/features/home/view/home_screen.dart index f25dcd5..f3fea3d 100644 --- a/agrilink_vocpro/lib/features/home/view/home_screen.dart +++ b/agrilink_vocpro/lib/features/home/view/home_screen.dart @@ -5,6 +5,7 @@ import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk1.dart'; import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk2.dart'; import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_dht.dart'; +import 'package:agrilink_vocpro/features/setting/provider/setting_provider.dart'; import 'package:animated_segmented_tab_control/animated_segmented_tab_control.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -29,96 +30,99 @@ class _HomeScreenState extends State { scrolledUnderElevation: 0, flexibleSpace: Padding( padding: EdgeInsets.symmetric(horizontal: 12.w), - child: Column( - children: [ - Row( - children: [ - CircleAvatar( - radius: 24.r, - backgroundColor: AppColor.primary, - child: Icon( - Icons.person, - color: Colors.white, - size: 24.r, - ), - ), - SizedBox(width: 16.w), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - getGreeting(DateTime.now().toString()), - style: AppTheme.labelSmall, + child: + Consumer(builder: (context, settingP, child) { + return Column( + children: [ + Row( + children: [ + CircleAvatar( + radius: 24.r, + backgroundColor: AppColor.primary, + child: Icon( + Icons.person, + color: Colors.white, + size: 24.r, ), - Text( - 'Fikril Mahesaputra', - style: AppTheme.labelMedium, + ), + SizedBox(width: 16.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + getGreeting(DateTime.now().toString()), + style: AppTheme.labelSmall, + ), + Text( + settingP.userFullName, + style: AppTheme.labelMedium, + ), + ], + ), + const Spacer(), + IconButton( + onPressed: () { + context.read().getLatestData(); + }, + icon: const Icon( + Icons.refresh_rounded, + color: AppColor.primary, + ), + ) + ], + ), + SizedBox( + height: 8.h, + ), + Container( + padding: const EdgeInsets.all(16), + height: MediaQuery.of(context).size.height * 0.17, + decoration: BoxDecoration( + color: AppColor.secondary, + image: const DecorationImage( + image: + AssetImage('assets/images/green_house_image.jpg'), + fit: BoxFit.cover), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: AppColor.ternary.withAlpha(200), + borderRadius: BorderRadius.circular(32), + ), + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("${settingP.userFullName}'s", + style: AppTheme.labelMedium + .copyWith(color: Colors.white)), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: AppColor.primary, + borderRadius: BorderRadius.circular(16), + ), + child: Text( + dateFormater(DateTime.now().toString()), + style: AppTheme.labelMedium + .copyWith(color: Colors.white), + ), + ), + ], + ), ), ], ), - const Spacer(), - IconButton( - onPressed: () { - context.read().getLatestData(); - }, - icon: const Icon( - Icons.refresh_rounded, - color: AppColor.primary, - ), - ) - ], - ), - SizedBox( - height: 8.h, - ), - Container( - padding: const EdgeInsets.all(16), - height: MediaQuery.of(context).size.height * 0.17, - decoration: BoxDecoration( - color: AppColor.secondary, - image: const DecorationImage( - image: - AssetImage('assets/images/green_house_image.jpg'), - fit: BoxFit.cover), - borderRadius: BorderRadius.circular(16), ), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: AppColor.ternary.withAlpha(200), - borderRadius: BorderRadius.circular(32), - ), - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("Fikril's Greenhouse", - style: AppTheme.labelMedium - .copyWith(color: Colors.white)), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: AppColor.primary, - borderRadius: BorderRadius.circular(16), - ), - child: Text( - dateFormater(DateTime.now().toString()), - style: AppTheme.labelMedium - .copyWith(color: Colors.white), - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), + ], + ); + }), ), ), body: DefaultTabController( diff --git a/agrilink_vocpro/lib/features/setting/provider/setting_provider.dart b/agrilink_vocpro/lib/features/setting/provider/setting_provider.dart index 6096f1f..d480b21 100644 --- a/agrilink_vocpro/lib/features/setting/provider/setting_provider.dart +++ b/agrilink_vocpro/lib/features/setting/provider/setting_provider.dart @@ -1,4 +1,6 @@ -import 'package:flutter/material.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/domain/service/app_service.dart'; +import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SettingProvider extends ChangeNotifier { @@ -15,4 +17,24 @@ class SettingProvider extends ChangeNotifier { userEmail = prefs.getString('email') ?? 'unknown'; notifyListeners(); } + + ResultState logoutState = ResultState.initial; + Future logout() async { + logoutState = ResultState.loading; + notifyListeners(); + try { + final result = await AppService().logout(); + if (result.data == null) { + logoutState = ResultState.hasData; + } else { + logoutState = ResultState.error; + } + } catch (e) { + if (kDebugMode) { + print('Error logout: $e'); + } + logoutState = ResultState.error; + } + notifyListeners(); + } } diff --git a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart index f8fa92d..b11c725 100644 --- a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart +++ b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart @@ -1,6 +1,7 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/route/app_route.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/features/setting/provider/setting_provider.dart'; import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:flutter/material.dart'; @@ -20,73 +21,72 @@ class SettingScreen extends StatelessWidget { backgroundColor: Colors.white, scrolledUnderElevation: 0, ), - body: SafeArea( - child: ListView( - padding: EdgeInsets.all(16.r), - children: [ - Row( - children: [ - const CircleAvatar( - radius: 30, - backgroundColor: AppColor.secondary, - child: Icon(BootstrapIcons.person_fill, color: Colors.white), - ), - SizedBox(width: 8.w), - Consumer(builder: (context, provider, child) { - return Column( + body: Consumer(builder: (context, provider, child) { + return SafeArea( + child: ListView( + padding: EdgeInsets.all(16.r), + children: [ + Row( + children: [ + const CircleAvatar( + radius: 30, + backgroundColor: AppColor.secondary, + child: Icon(BootstrapIcons.person_fill, color: Colors.white), + ), + SizedBox(width: 8.w), + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(provider.userFullName, style: AppTheme.labelMedium), Text(provider.userEmail, style: AppTheme.labelSmall) ], - ); - }) - ], - ), - SizedBox(height: 16.h), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.r), + ) + ], ), - child: Column( - children: [ - ListTile( - tileColor: Colors.white, - title: Text('Account', - style: - AppTheme.labelSmall.copyWith(color: Colors.black87)), - leading: const Icon(BootstrapIcons.person), - trailing: Icon( - Icons.arrow_forward_ios, - size: 16.r, + SizedBox(height: 16.h), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), + ), + child: Column( + children: [ + ListTile( + tileColor: Colors.white, + title: Text('Account', + style: AppTheme.labelSmall + .copyWith(color: Colors.black87)), + leading: const Icon(BootstrapIcons.person), + trailing: Icon( + Icons.arrow_forward_ios, + size: 16.r, + ), + onTap: () {}, ), - onTap: () {}, - ), - ListTile( - tileColor: Colors.white, - title: Text('Kebijakan & privasi', - style: - AppTheme.labelSmall.copyWith(color: Colors.black87)), - leading: const Icon(BootstrapIcons.shield_check), - trailing: Icon( - Icons.arrow_forward_ios, - size: 16.r, + ListTile( + tileColor: Colors.white, + title: Text('Kebijakan & privasi', + style: AppTheme.labelSmall + .copyWith(color: Colors.black87)), + leading: const Icon(BootstrapIcons.shield_check), + trailing: Icon( + Icons.arrow_forward_ios, + size: 16.r, + ), + onTap: () {}, ), - onTap: () {}, - ), - ListTile( - tileColor: Colors.white, - title: Text('Syarat & ketentuan', - style: - AppTheme.labelSmall.copyWith(color: Colors.black87)), - leading: const Icon(BootstrapIcons.file_text), - trailing: Icon( - Icons.arrow_forward_ios, - size: 16.r, + ListTile( + tileColor: Colors.white, + title: Text('Syarat & ketentuan', + style: AppTheme.labelSmall + .copyWith(color: Colors.black87)), + leading: const Icon(BootstrapIcons.file_text), + trailing: Icon( + Icons.arrow_forward_ios, + size: 16.r, + ), + onTap: () {}, ), - onTap: () {}, - ), - ListTile( + ListTile( tileColor: Colors.white, title: Text('Logout', style: AppTheme.labelSmall.copyWith(color: Colors.red)), @@ -113,19 +113,27 @@ class SettingScreen extends StatelessWidget { ), TextButton( child: Text('Ya'), - onPressed: () { - context.go(AppRoute.root); + onPressed: () async { + await provider.logout(); + if (context.mounted) { + if (provider.logoutState == + ResultState.hasData) { + context.go(AppRoute.root); + } + } }, ), ], ), ); - }), - ], + }, + ), + ], + ), ), - ), - ], - )), + ], + )); + }), ); } }