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: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())));
}

View File

@ -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);

View File

@ -75,7 +75,7 @@ class _NavigationPageState extends State<NavigationPage> {
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<NavigationPage> {
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<NavigationPage> {
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<NavigationPage> {
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)),
],
),
),

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: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: () {},
// ),
],
),
);

View File

@ -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<HomeScreen> {
@override
Widget build(BuildContext context) {
return const Scaffold(
return Scaffold(
backgroundColor: whiteColor,
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 16),

View File

@ -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<ProfilScreen> {
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<LogoutViewModel>(
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),
),
],
),
],
),
);
},
),
);
}

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/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<LoginViewModel>()),
ChangeNotifierProvider(create: (_) => sl<OtpViewModel>()),
ChangeNotifierProvider(create: (_) => sl<LogoutViewModel>()),
],
child: ScreenUtilInit(
designSize: const Size(375, 812),

View File

@ -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,

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:
- server/.env.dev
- assets/image/
- assets/icon/