feat: logout behavior

This commit is contained in:
pahmiudahgede 2025-05-14 17:00:40 +07:00
parent 801698262b
commit 438c95746f
14 changed files with 211 additions and 47 deletions

View File

@ -1,9 +1,12 @@
import 'package:get_it/get_it.dart'; 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/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/presentation/viewmodel/otp_vmod.dart';
import 'package:rijig_mobile/features/auth/repositories/login_repository.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/repositories/otp_repository.dart';
import 'package:rijig_mobile/features/auth/service/login_service.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'; import 'package:rijig_mobile/features/auth/service/otp_service.dart';
final sl = GetIt.instance; final sl = GetIt.instance;
@ -11,4 +14,5 @@ final sl = GetIt.instance;
void init() { void init() {
sl.registerFactory(() => LoginViewModel(LoginService(LoginRepository()))); sl.registerFactory(() => LoginViewModel(LoginService(LoginRepository())));
sl.registerFactory(() => OtpViewModel(OtpService(OtpRepository()))); sl.registerFactory(() => OtpViewModel(OtpService(OtpRepository())));
sl.registerFactory(() => LogoutViewModel(LogoutService(LogoutRepository())));
} }

View File

@ -3,7 +3,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
// =====================color schema===================== // =====================color schema=====================
Color whiteColor = Color(0xffF0F1EA); Color whiteColor = Color(0xffFBFBFB);
Color blackNavyColor = Color(0xff101010); Color blackNavyColor = Color(0xff101010);
Color primaryColor = Color(0xff018558); Color primaryColor = Color(0xff018558);
Color secondaryColor = Color(0xffBDE902); Color secondaryColor = Color(0xffBDE902);

View File

@ -75,7 +75,7 @@ class _NavigationPageState extends State<NavigationPage> {
padding: PaddingCustom().paddingHorizontal(2), padding: PaddingCustom().paddingHorizontal(2),
elevation: 0, elevation: 0,
height: 67, height: 67,
color: Colors.white, color: primaryColor,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
notchMargin: 3.0, notchMargin: 3.0,
child: BottomNavigationBar( child: BottomNavigationBar(
@ -84,33 +84,33 @@ class _NavigationPageState extends State<NavigationPage> {
elevation: 0, elevation: 0,
showSelectedLabels: true, showSelectedLabels: true,
showUnselectedLabels: true, showUnselectedLabels: true,
selectedItemColor: Colors.blue, selectedItemColor: secondaryColor,
unselectedItemColor: Colors.grey, unselectedItemColor: whiteColor,
currentIndex: _selectedIndex, currentIndex: _selectedIndex,
onTap: _onItemTapped, onTap: _onItemTapped,
items: [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Iconsax.home_1, color: Colors.grey), icon: Icon(Iconsax.home_2),
activeIcon: Icon(Iconsax.home_1, color: Colors.blue), activeIcon: Icon(Iconsax.home_2, size: 28),
label: 'Beranda', label: 'Beranda',
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Iconsax.home, color: Colors.grey), icon: Icon(Iconsax.note_favorite),
activeIcon: Icon(Iconsax.home, color: Colors.blue), activeIcon: Icon(Iconsax.note_favorite, size: 28),
label: 'Pesan', label: 'Aktivitas',
), ),
const BottomNavigationBarItem( const BottomNavigationBarItem(
icon: SizedBox.shrink(), icon: SizedBox.shrink(),
label: '', label: '',
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Iconsax.document, color: Colors.grey), icon: Icon(Iconsax.shopping_cart),
activeIcon: Icon(Iconsax.document, color: Colors.blue), activeIcon: Icon(Iconsax.shopping_cart, size: 28),
label: 'Tutorial', label: 'Keranjang',
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Iconsax.home, color: Colors.grey), icon: Icon(Iconsax.user),
activeIcon: Icon(Iconsax.home, color: Colors.blue), activeIcon: Icon(Iconsax.user, size: 28),
label: 'Profil', label: 'Profil',
), ),
], ],
@ -129,7 +129,7 @@ class _NavigationPageState extends State<NavigationPage> {
onPressed: () { onPressed: () {
router.push("/requestpickup"); router.push("/requestpickup");
}, },
backgroundColor: Colors.white, backgroundColor: primaryColor,
shape: const CircleBorder( shape: const CircleBorder(
side: BorderSide(color: Colors.white, width: 4), side: BorderSide(color: Colors.white, width: 4),
), ),
@ -142,8 +142,8 @@ class _NavigationPageState extends State<NavigationPage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
Icon(Iconsax.home, color: primaryColor, size: 30), Icon(Iconsax.archive_2, color: whiteColor, size: 30),
Text("data", style: TextStyle(color: blackNavyColor)), Text("mulai", style: TextStyle(color: whiteColor)),
], ],
), ),
), ),

View File

@ -0,0 +1,9 @@
class LogoutResponse {
final String message;
LogoutResponse({required this.message});
factory LogoutResponse.fromJson(Map<String, dynamic> json) {
return LogoutResponse(message: json['meta']['message']);
}
}

View File

@ -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<void> logout() async {
isLoading = true;
errorMessage = null;
notifyListeners();
try {
await _logoutService.logout();
} catch (e) {
errorMessage = "Error: ${e.toString()}";
}
isLoading = false;
notifyListeners();
}
}

View File

@ -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<LogoutResponse> logout() async {
final response = await _https.post('/authmasyarakat/logout');
return LogoutResponse.fromJson(response);
}
}

View File

@ -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<void> clearSession() async {
await _storage.deleteSecureData('token');
await _storage.deleteSecureData('user_id');
await _storage.deleteSecureData('user_role');
}
Future<void> logout() async {
try {
await _logoutRepository.logout();
await clearSession();
} catch (e) {
throw Exception('Logout failed: $e');
}
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:iconsax_flutter/iconsax_flutter.dart'; import 'package:iconsax_flutter/iconsax_flutter.dart';
import 'package:rijig_mobile/core/utils/guide.dart';
class HomeHeader extends StatelessWidget { class HomeHeader extends StatelessWidget {
const HomeHeader({super.key}); const HomeHeader({super.key});
@ -12,33 +13,23 @@ class HomeHeader extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Expanded( Expanded(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text("Rijig", style: TextStyle(fontSize: 24)), Text("Rijig", style: Tulisan.heading(color: primaryColor)),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Icon(Iconsax.notification), Icon(Iconsax.notification),
Gap(10), Gap(10),
Icon(Iconsax.message), Icon(Iconsax.message_2),
], ],
), ),
], ],
), ),
), ),
const SizedBox(width: 16), 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: () {},
// ),
], ],
), ),
); );

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; 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/categories.dart';
import 'package:rijig_mobile/features/home/components/discount_banner.dart'; import 'package:rijig_mobile/features/home/components/discount_banner.dart';
import 'package:rijig_mobile/features/home/components/home_header.dart'; import 'package:rijig_mobile/features/home/components/home_header.dart';
@ -15,7 +16,8 @@ class HomeScreen extends StatefulWidget {
class _HomeScreenState extends State<HomeScreen> { class _HomeScreenState extends State<HomeScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Scaffold( return Scaffold(
backgroundColor: whiteColor,
body: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 16), padding: EdgeInsets.symmetric(vertical: 16),

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/router.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 { class ProfilScreen extends StatefulWidget {
const ProfilScreen({super.key}); const ProfilScreen({super.key});
@ -14,21 +16,45 @@ class _ProfilScreenState extends State<ProfilScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final titleofscreen = "Profil"; final titleofscreen = "Profil";
return Scaffold( return Scaffold(
body: Center( body: Consumer<LogoutViewModel>(
builder: (context, viewModel, child) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text("ini adalah halaman $titleofscreen"), Text("Are you sure you want to logout?"),
TextButton( SizedBox(height: 20),
onPressed: () async { CardButtonOne(
SharedPreferences prefs = await SharedPreferences.getInstance(); textButton: viewModel.isLoading ? 'Logging out...' : 'Logout',
await prefs.remove('isLoggedIn'); fontSized: 16,
router.go('/login'); 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!)),
);
}
}, },
child: Text("keluar"), loadingTrue: viewModel.isLoading,
usingRow: false,
),
if (viewModel.errorMessage != null)
Text(
viewModel.errorMessage!,
style: TextStyle(color: Colors.red),
), ),
], ],
), ),
);
},
), ),
); );
} }

View File

@ -8,6 +8,7 @@ import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/container/injection_container.dart'; import 'package:rijig_mobile/core/container/injection_container.dart';
import 'package:rijig_mobile/core/router.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/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/presentation/viewmodel/otp_vmod.dart';
void main() async { void main() async {
@ -29,6 +30,7 @@ class MyApp extends StatelessWidget {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => sl<LoginViewModel>()), ChangeNotifierProvider(create: (_) => sl<LoginViewModel>()),
ChangeNotifierProvider(create: (_) => sl<OtpViewModel>()), ChangeNotifierProvider(create: (_) => sl<OtpViewModel>()),
ChangeNotifierProvider(create: (_) => sl<LogoutViewModel>()),
], ],
child: ScreenUtilInit( child: ScreenUtilInit(
designSize: const Size(375, 812), designSize: const Size(375, 812),

View File

@ -19,7 +19,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
onPressed: () { onPressed: () {
router.pop(); 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, backgroundColor: primaryColor,
elevation: 0, elevation: 0,

View File

@ -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),
),
],
),
],
),
);
}
}

View File

@ -41,3 +41,4 @@ flutter:
assets: assets:
- server/.env.dev - server/.env.dev
- assets/image/ - assets/image/
- assets/icon/