diff --git a/lib/core/container/injection_container.dart b/lib/core/container/injection_container.dart index 2389e2f..204c492 100644 --- a/lib/core/container/injection_container.dart +++ b/lib/core/container/injection_container.dart @@ -1,9 +1,12 @@ import 'package:get_it/get_it.dart'; import 'package:rijig_mobile/features/auth/presentation/viewmodel/login_vmod.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/logout_vmod.dart'; import 'package:rijig_mobile/features/auth/presentation/viewmodel/otp_vmod.dart'; import 'package:rijig_mobile/features/auth/repositories/login_repository.dart'; +import 'package:rijig_mobile/features/auth/repositories/logout_repository.dart'; import 'package:rijig_mobile/features/auth/repositories/otp_repository.dart'; import 'package:rijig_mobile/features/auth/service/login_service.dart'; +import 'package:rijig_mobile/features/auth/service/logout_service.dart'; import 'package:rijig_mobile/features/auth/service/otp_service.dart'; final sl = GetIt.instance; @@ -11,4 +14,5 @@ final sl = GetIt.instance; void init() { sl.registerFactory(() => LoginViewModel(LoginService(LoginRepository()))); sl.registerFactory(() => OtpViewModel(OtpService(OtpRepository()))); + sl.registerFactory(() => LogoutViewModel(LogoutService(LogoutRepository()))); } diff --git a/lib/core/utils/guide.dart b/lib/core/utils/guide.dart index d3e3f36..d701a37 100644 --- a/lib/core/utils/guide.dart +++ b/lib/core/utils/guide.dart @@ -3,7 +3,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; // =====================color schema===================== -Color whiteColor = Color(0xffF0F1EA); +Color whiteColor = Color(0xffFBFBFB); Color blackNavyColor = Color(0xff101010); Color primaryColor = Color(0xff018558); Color secondaryColor = Color(0xffBDE902); diff --git a/lib/core/utils/navigation.dart b/lib/core/utils/navigation.dart index f528869..9c9c79a 100644 --- a/lib/core/utils/navigation.dart +++ b/lib/core/utils/navigation.dart @@ -75,7 +75,7 @@ class _NavigationPageState extends State { padding: PaddingCustom().paddingHorizontal(2), elevation: 0, height: 67, - color: Colors.white, + color: primaryColor, clipBehavior: Clip.antiAlias, notchMargin: 3.0, child: BottomNavigationBar( @@ -84,33 +84,33 @@ class _NavigationPageState extends State { elevation: 0, showSelectedLabels: true, showUnselectedLabels: true, - selectedItemColor: Colors.blue, - unselectedItemColor: Colors.grey, + selectedItemColor: secondaryColor, + unselectedItemColor: whiteColor, currentIndex: _selectedIndex, onTap: _onItemTapped, items: [ BottomNavigationBarItem( - icon: Icon(Iconsax.home_1, color: Colors.grey), - activeIcon: Icon(Iconsax.home_1, color: Colors.blue), + icon: Icon(Iconsax.home_2), + activeIcon: Icon(Iconsax.home_2, size: 28), label: 'Beranda', ), BottomNavigationBarItem( - icon: Icon(Iconsax.home, color: Colors.grey), - activeIcon: Icon(Iconsax.home, color: Colors.blue), - label: 'Pesan', + icon: Icon(Iconsax.note_favorite), + activeIcon: Icon(Iconsax.note_favorite, size: 28), + label: 'Aktivitas', ), const BottomNavigationBarItem( icon: SizedBox.shrink(), label: '', ), BottomNavigationBarItem( - icon: Icon(Iconsax.document, color: Colors.grey), - activeIcon: Icon(Iconsax.document, color: Colors.blue), - label: 'Tutorial', + icon: Icon(Iconsax.shopping_cart), + activeIcon: Icon(Iconsax.shopping_cart, size: 28), + label: 'Keranjang', ), BottomNavigationBarItem( - icon: Icon(Iconsax.home, color: Colors.grey), - activeIcon: Icon(Iconsax.home, color: Colors.blue), + icon: Icon(Iconsax.user), + activeIcon: Icon(Iconsax.user, size: 28), label: 'Profil', ), ], @@ -129,7 +129,7 @@ class _NavigationPageState extends State { onPressed: () { router.push("/requestpickup"); }, - backgroundColor: Colors.white, + backgroundColor: primaryColor, shape: const CircleBorder( side: BorderSide(color: Colors.white, width: 4), ), @@ -142,8 +142,8 @@ class _NavigationPageState extends State { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - Icon(Iconsax.home, color: primaryColor, size: 30), - Text("data", style: TextStyle(color: blackNavyColor)), + Icon(Iconsax.archive_2, color: whiteColor, size: 30), + Text("mulai", style: TextStyle(color: whiteColor)), ], ), ), diff --git a/lib/features/auth/model/logout_model.dart b/lib/features/auth/model/logout_model.dart new file mode 100644 index 0000000..3140df4 --- /dev/null +++ b/lib/features/auth/model/logout_model.dart @@ -0,0 +1,9 @@ +class LogoutResponse { + final String message; + + LogoutResponse({required this.message}); + + factory LogoutResponse.fromJson(Map json) { + return LogoutResponse(message: json['meta']['message']); + } +} diff --git a/lib/features/auth/presentation/viewmodel/logout_vmod.dart b/lib/features/auth/presentation/viewmodel/logout_vmod.dart new file mode 100644 index 0000000..c315387 --- /dev/null +++ b/lib/features/auth/presentation/viewmodel/logout_vmod.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/features/auth/service/logout_service.dart'; + +class LogoutViewModel extends ChangeNotifier { + final LogoutService _logoutService; + + LogoutViewModel(this._logoutService); + + bool isLoading = false; + String? errorMessage; + + Future logout() async { + isLoading = true; + errorMessage = null; + notifyListeners(); + + try { + await _logoutService.logout(); + } catch (e) { + errorMessage = "Error: ${e.toString()}"; + } + + isLoading = false; + notifyListeners(); + } +} diff --git a/lib/features/auth/repositories/logout_repository.dart b/lib/features/auth/repositories/logout_repository.dart new file mode 100644 index 0000000..d5b128f --- /dev/null +++ b/lib/features/auth/repositories/logout_repository.dart @@ -0,0 +1,11 @@ +import 'package:rijig_mobile/core/api/api_services.dart'; +import 'package:rijig_mobile/features/auth/model/logout_model.dart'; + +class LogoutRepository { + final Https _https = Https(); + + Future logout() async { + final response = await _https.post('/authmasyarakat/logout'); + return LogoutResponse.fromJson(response); + } +} diff --git a/lib/features/auth/service/logout_service.dart b/lib/features/auth/service/logout_service.dart new file mode 100644 index 0000000..2b5049f --- /dev/null +++ b/lib/features/auth/service/logout_service.dart @@ -0,0 +1,24 @@ +import 'package:rijig_mobile/core/storage/secure_storage.dart'; +import 'package:rijig_mobile/features/auth/repositories/logout_repository.dart'; + +class LogoutService { + final LogoutRepository _logoutRepository; + final _storage = SecureStorage(); + + LogoutService(this._logoutRepository); + + Future clearSession() async { + await _storage.deleteSecureData('token'); + await _storage.deleteSecureData('user_id'); + await _storage.deleteSecureData('user_role'); + } + + Future logout() async { + try { + await _logoutRepository.logout(); + await clearSession(); + } catch (e) { + throw Exception('Logout failed: $e'); + } + } +} diff --git a/lib/features/home/components/home_header.dart b/lib/features/home/components/home_header.dart index c6f104a..b038bcd 100644 --- a/lib/features/home/components/home_header.dart +++ b/lib/features/home/components/home_header.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:iconsax_flutter/iconsax_flutter.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; class HomeHeader extends StatelessWidget { const HomeHeader({super.key}); @@ -12,33 +13,23 @@ class HomeHeader extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Expanded( + Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Rijig", style: TextStyle(fontSize: 24)), + Text("Rijig", style: Tulisan.heading(color: primaryColor)), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon(Iconsax.notification), Gap(10), - Icon(Iconsax.message), + Icon(Iconsax.message_2), ], ), ], ), ), const SizedBox(width: 16), - // IconBtnWithCounter( - // svgSrc: "assets/icons/Cart Icon.svg", - // press: () => Navigator.pushNamed(context, CartScreen.routeName), - // ), - // const SizedBox(width: 8), - // IconBtnWithCounter( - // svgSrc: "assets/icons/Bell.svg", - // numOfitem: 3, - // press: () {}, - // ), ], ), ); diff --git a/lib/features/home/home_screen.dart b/lib/features/home/home_screen.dart index 88e22dc..2df69e4 100644 --- a/lib/features/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/features/home/components/categories.dart'; import 'package:rijig_mobile/features/home/components/discount_banner.dart'; import 'package:rijig_mobile/features/home/components/home_header.dart'; @@ -15,7 +16,8 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( + backgroundColor: whiteColor, body: SafeArea( child: SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 16), diff --git a/lib/features/profil/profil_screen.dart b/lib/features/profil/profil_screen.dart index 8e3f7a0..36697ab 100644 --- a/lib/features/profil/profil_screen.dart +++ b/lib/features/profil/profil_screen.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:rijig_mobile/core/router.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/logout_vmod.dart'; +import 'package:rijig_mobile/widget/buttoncard.dart'; class ProfilScreen extends StatefulWidget { const ProfilScreen({super.key}); @@ -14,21 +16,45 @@ class _ProfilScreenState extends State { Widget build(BuildContext context) { final titleofscreen = "Profil"; return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("ini adalah halaman $titleofscreen"), - TextButton( - onPressed: () async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.remove('isLoggedIn'); - router.go('/login'); - }, - child: Text("keluar"), + body: Consumer( + builder: (context, viewModel, child) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Are you sure you want to logout?"), + SizedBox(height: 20), + CardButtonOne( + textButton: viewModel.isLoading ? 'Logging out...' : 'Logout', + fontSized: 16, + colorText: Colors.white, + borderRadius: 10, + horizontal: double.infinity, + vertical: 50, + onTap: () async { + await viewModel.logout(); + + if (viewModel.errorMessage == null) { + router.go("/login"); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(viewModel.errorMessage!)), + ); + } + }, + loadingTrue: viewModel.isLoading, + usingRow: false, + ), + if (viewModel.errorMessage != null) + Text( + viewModel.errorMessage!, + style: TextStyle(color: Colors.red), + ), + ], ), - ], - ), + ); + }, ), ); } diff --git a/lib/main.dart b/lib/main.dart index fa16e35..5c5bcb6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:provider/provider.dart'; import 'package:rijig_mobile/core/container/injection_container.dart'; import 'package:rijig_mobile/core/router.dart'; import 'package:rijig_mobile/features/auth/presentation/viewmodel/login_vmod.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/logout_vmod.dart'; import 'package:rijig_mobile/features/auth/presentation/viewmodel/otp_vmod.dart'; void main() async { @@ -29,6 +30,7 @@ class MyApp extends StatelessWidget { providers: [ ChangeNotifierProvider(create: (_) => sl()), ChangeNotifierProvider(create: (_) => sl()), + ChangeNotifierProvider(create: (_) => sl()), ], child: ScreenUtilInit( designSize: const Size(375, 812), diff --git a/lib/widget/appbar.dart b/lib/widget/appbar.dart index 641b51b..8d57d93 100644 --- a/lib/widget/appbar.dart +++ b/lib/widget/appbar.dart @@ -19,7 +19,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { onPressed: () { router.pop(); }, - icon: Icon(Iconsax.arrow_left_3_copy, color: whiteColor, size: 26), + icon: Icon(Iconsax.arrow_circle_left, color: whiteColor, size: 26), ), backgroundColor: primaryColor, elevation: 0, diff --git a/lib/widget/information_card.dart b/lib/widget/information_card.dart new file mode 100644 index 0000000..2cac150 --- /dev/null +++ b/lib/widget/information_card.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +class InformationCard extends StatelessWidget { + const InformationCard({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 120, + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: const Color(0xFF1F1F1F), + borderRadius: BorderRadius.circular(12.0), + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Membership', + style: TextStyle( + color: Colors.white, + fontSize: 18.0, + fontWeight: FontWeight.w600, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '594', + style: TextStyle( + color: Colors.white, + fontSize: 27.0, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'US\$496', + style: TextStyle( + color: Colors.white, + fontSize: 27.0, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total members', + style: TextStyle( + color: Colors.white70, + fontSize: 16.0, + fontWeight: FontWeight.w600, + ), + ), + Text( + 'Per month', + style: TextStyle(color: Colors.white70, fontSize: 17.0), + ), + ], + ), + ], + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 795985b..33ca4ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,3 +41,4 @@ flutter: assets: - server/.env.dev - assets/image/ + - assets/icon/