refact: device_id for manage access, and bug: userpin runned unsucces

This commit is contained in:
pahmiudahgede 2025-05-03 03:30:18 +07:00
parent de39c9d7fd
commit f620157de8
13 changed files with 340 additions and 26 deletions

View File

@ -0,0 +1,23 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:uuid/uuid.dart';
import 'dart:io';
Future<String> getDeviceId() async {
final deviceInfo = DeviceInfoPlugin();
String deviceID;
if (Platform.isAndroid) {
var androidInfo = await deviceInfo.androidInfo;
deviceID = androidInfo.id;
} else if (Platform.isIOS) {
var iosInfo = await deviceInfo.iosInfo;
deviceID = iosInfo.identifierForVendor ?? '';
} else {
var uuid = Uuid();
deviceID = uuid.v4();
}
return deviceID;
}

View File

@ -5,8 +5,10 @@ import 'package:rijig_mobile/screen/app/cart/cart_screen.dart';
import 'package:rijig_mobile/screen/app/home/home_screen.dart';
import 'package:rijig_mobile/screen/app/profil/profil_screen.dart';
import 'package:rijig_mobile/screen/app/requestpick/requestpickup_screen.dart';
import 'package:rijig_mobile/screen/auth/inputpin_screen.dart';
import 'package:rijig_mobile/screen/auth/login_screen.dart';
import 'package:rijig_mobile/screen/auth/otp_screen.dart';
import 'package:rijig_mobile/screen/auth/verifpin_screen.dart';
import 'package:rijig_mobile/screen/launch/onboardingpage_screen.dart';
import 'package:rijig_mobile/screen/launch/splash_screen.dart';
@ -25,10 +27,8 @@ final router = GoRouter(
return VerifotpScreen(phone: phone!);
},
),
GoRoute(
path: '/inputpin',
builder: (context, state) => OnboardingPageScreen(),
),
GoRoute(path: '/setpin', builder: (context, state) => UserPinScreen()),
GoRoute(path: '/verifpin', builder: (context, state) => VerifPinScreen()),
GoRoute(
path: '/navigasi',
builder: (context, state) {

View File

@ -7,6 +7,7 @@ import 'package:intl/date_symbol_data_local.dart';
import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/router.dart';
import 'package:rijig_mobile/viewmodel/auth_vmod.dart';
import 'package:rijig_mobile/viewmodel/userpin_vmod.dart'; // Import PinViewModel
void main() async {
await dotenv.load(fileName: "server/.env.dev");
@ -24,14 +25,17 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AuthViewModel(),
child: ScreenUtilInit(
designSize: const Size(375, 812),
builder: (_, child) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: router,
);
},
child: ChangeNotifierProvider(
create: (_) => PinViewModel(), // Tambahkan PinViewModel
child: ScreenUtilInit(
designSize: const Size(375, 812),
builder: (_, child) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: router,
);
},
),
),
);
}

View File

@ -15,11 +15,16 @@ class AuthModel {
}
}
Future<ResponseModel?> verifyOtp(String phone, String otp) async {
Future<ResponseModel?> verifyOtp(
String phone,
String otp,
String deviceId,
) async {
try {
var response = await _apiService.post('/authmasyarakat/verify-otp', {
'phone': phone,
'otp': otp,
'device_id': deviceId,
});
return ResponseModel.fromJson(response);
} catch (e) {

View File

@ -1,25 +1,34 @@
import 'package:rijig_mobile/core/api_services.dart';
import 'package:rijig_mobile/model/response_model.dart';
class AuthService {
class PinModel {
final ApiService _apiService = ApiService();
Future<ResponseModel?> cekPinStatus(String userid) async {
Future<bool> checkPinStatus() async {
try {
var response = await _apiService.get('/cek-pin-status');
return ResponseModel.fromJson(response);
if (response['meta']['status'] == 200) {
return true;
} else {
return false;
}
} catch (e) {
rethrow;
}
}
Future<Map<String, dynamic>> verifyOtp(String phone, String otp) async {
Future<bool> setPin(String userPin) async {
try {
var response = await _apiService.post('/authmasyarakat/verify-otp', {
'phone': phone,
'otp': otp,
});
return response;
var response = await _apiService.post('/set-pin', {'userpin': userPin});
return response['meta']['status'] == 201;
} catch (e) {
rethrow;
}
}
Future<bool> verifyPin(String userPin) async {
try {
var response = await _apiService.post('/verif-pin', {'userpin': userPin});
return response['meta']['status'] == 200;
} catch (e) {
rethrow;
}

View File

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/guide.dart';
import 'package:rijig_mobile/core/router.dart';
import 'package:rijig_mobile/viewmodel/userpin_vmod.dart';
class UserPinScreen extends StatefulWidget {
const UserPinScreen({super.key});
@override
State<UserPinScreen> createState() => _UserPinScreenState();
}
class _UserPinScreenState extends State<UserPinScreen> {
final TextEditingController _pinController = TextEditingController();
final _formKey = GlobalKey<FormState>();
String errorMessage = '';
@override
Widget build(BuildContext context) {
final pinViewModel = Provider.of<PinViewModel>(context);
return Scaffold(
appBar: AppBar(title: const Text("Buat PIN Pengguna")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _pinController,
decoration: const InputDecoration(labelText: 'Masukkan PIN'),
obscureText: true,
maxLength: 6,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'PIN harus diisi';
} else if (value.length != 6) {
return 'PIN harus 6 digit';
}
return null;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState?.validate() ?? false) {
final pin = _pinController.text;
bool pinCreated = await pinViewModel.checkPinStatus();
if (!pinCreated) {
bool pinSet = await pinViewModel.setPin(pin);
if (pinSet) {
router.go('/navigasi');
} else {
setState(() {
errorMessage = 'Gagal membuat PIN';
});
}
} else {
bool pinVerified = await pinViewModel.verifyPin(pin);
if (pinVerified) {
router.go('/navigasi');
} else {
setState(() {
errorMessage = 'PIN yang anda masukkan salah';
});
}
}
}
},
child: const Text('Kirim'),
),
if (errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(errorMessage, style: TextStyle(color: redColor)),
),
],
),
),
),
);
}
}

View File

@ -58,11 +58,26 @@ class _VerifotpScreenState extends State<VerifotpScreen> {
horizontal: double.infinity,
vertical: 50,
onTap: () {
// if (_otpController.text.isNotEmpty) {
// viewModel.verifyOtp(widget.phone, _otpController.text).then(
// (_) {
// if (viewModel.errorMessage == null) {
// router.go('/navigasi');
// } else {
// debugPrint(viewModel.errorMessage ?? '');
// }
// },
// );
// }
if (_otpController.text.isNotEmpty) {
viewModel.verifyOtp(widget.phone, _otpController.text).then(
(_) {
if (viewModel.errorMessage == null) {
router.go('/navigasi');
if (viewModel.pinExists == false) {
router.go('/setpin');
} else {
router.go('/verifpin');
}
} else {
debugPrint(viewModel.errorMessage ?? '');
}

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/router.dart';
import 'package:rijig_mobile/viewmodel/userpin_vmod.dart';
class VerifPinScreen extends StatefulWidget {
const VerifPinScreen({super.key});
@override
State<VerifPinScreen> createState() => _VerifPinScreenState();
}
class _VerifPinScreenState extends State<VerifPinScreen> {
final TextEditingController _pinController = TextEditingController();
final _formKey = GlobalKey<FormState>();
String errorMessage = '';
@override
Widget build(BuildContext context) {
final pinViewModel = Provider.of<PinViewModel>(context);
return Scaffold(
appBar: AppBar(title: const Text("Verifikasi PIN")),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _pinController,
decoration: const InputDecoration(labelText: 'Masukkan PIN'),
obscureText: true,
maxLength: 6,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'PIN harus diisi';
} else if (value.length != 6) {
return 'PIN harus 6 digit';
}
return null;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState?.validate() ?? false) {
final pin = _pinController.text;
bool pinVerified = await pinViewModel.verifyPin(pin);
if (pinVerified) {
router.go('/navigasi');
} else {
setState(() {
errorMessage = 'PIN yang Anda masukkan salah';
});
}
}
},
child: const Text('Kirim'),
),
if (errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(
errorMessage,
style: const TextStyle(color: Colors.red),
),
),
],
),
),
),
),
);
}
}

View File

@ -1,15 +1,19 @@
import 'package:flutter/material.dart';
import 'package:rijig_mobile/core/getinfodevice.dart';
import 'package:rijig_mobile/model/response_model.dart';
import 'package:rijig_mobile/model/auth_model.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:rijig_mobile/model/userpin_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthViewModel extends ChangeNotifier {
final AuthModel _authModel = AuthModel();
final PinModel _pinModel = PinModel();
final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
bool isLoading = false;
String? errorMessage;
ResponseModel? authModel;
bool? pinExists;
Future<void> login(String phone) async {
try {
@ -22,7 +26,6 @@ class AuthViewModel extends ChangeNotifier {
if (response != null && response.status == 200) {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('isLoggedIn', false);
authModel = response;
} else {
errorMessage = response?.message ?? 'Failed to send OTP';
@ -41,7 +44,9 @@ class AuthViewModel extends ChangeNotifier {
errorMessage = null;
notifyListeners();
var response = await _authModel.verifyOtp(phone, otp);
String deviceId = await getDeviceId();
var response = await _authModel.verifyOtp(phone, otp, deviceId);
if (response != null && response.status == 200) {
await _secureStorage.write(
@ -59,8 +64,10 @@ class AuthViewModel extends ChangeNotifier {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('isLoggedIn', true);
pinExists = await _pinModel.checkPinStatus();
authModel = response;
notifyListeners();
} else {
errorMessage = response?.message ?? 'Failed to verify OTP';
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:rijig_mobile/model/userpin_model.dart';
class PinViewModel extends ChangeNotifier {
final PinModel _pinService = PinModel();
Future<bool> checkPinStatus() async {
try {
return await _pinService.checkPinStatus();
} catch (e) {
throw Exception('Error checking PIN status: $e');
}
}
Future<bool> setPin(String userPin) async {
try {
return await _pinService.setPin(userPin);
} catch (e) {
throw Exception('Error setting PIN: $e');
}
}
Future<bool> verifyPin(String userPin) async {
try {
return await _pinService.verifyPin(userPin);
} catch (e) {
throw Exception('Error verifying PIN: $e');
}
}
}

View File

@ -5,11 +5,13 @@
import FlutterMacOS
import Foundation
import device_info_plus
import flutter_secure_storage_macos
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@ -73,6 +73,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
device_info_plus:
dependency: "direct main"
description:
name: device_info_plus
sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53"
url: "https://pub.dev"
source: hosted
version: "11.4.0"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
fake_async:
dependency: transitive
description:
@ -97,6 +113,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
@ -501,6 +525,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@ -549,6 +581,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
uuid:
dependency: "direct main"
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_graphics:
dependency: transitive
description:
@ -605,6 +645,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.12.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
xdg_directories:
dependency: transitive
description:

View File

@ -10,6 +10,7 @@ environment:
dependencies:
concentric_transition: ^1.0.3
cupertino_icons: ^1.0.8
device_info_plus: ^11.4.0
flutter:
sdk: flutter
flutter_dotenv: ^5.2.1
@ -25,6 +26,7 @@ dependencies:
pin_code_fields: ^8.0.1
provider: ^6.1.4
shared_preferences: ^2.3.3
uuid: ^4.5.1
dev_dependencies:
flutter_lints: ^5.0.0