diff --git a/lib/core/api/api_exception.dart b/lib/core/api/api_exception.dart new file mode 100644 index 0000000..2500bb7 --- /dev/null +++ b/lib/core/api/api_exception.dart @@ -0,0 +1,18 @@ +class ApiException implements Exception { + final String message; + final int statusCode; + ApiException(this.message, this.statusCode); + + @override + String toString() { + return "ApiException: $message (Status Code: $statusCode)"; + } +} + +class NetworkException implements Exception { + final String message; + NetworkException(this.message); + + @override + String toString() => 'NetworkException: $message'; +} diff --git a/lib/core/api/api_services.dart b/lib/core/api/api_services.dart new file mode 100644 index 0000000..d443d63 --- /dev/null +++ b/lib/core/api/api_services.dart @@ -0,0 +1,192 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:http/http.dart' as http; +import 'package:rijig_mobile/core/api/api_exception.dart'; +import 'package:rijig_mobile/core/storage/expired_token.dart'; +import 'package:rijig_mobile/core/storage/secure_storage.dart'; + +class Https { + static final Https _instance = Https.internal(); + Https.internal(); + factory Https() => _instance; + + final String? _baseUrl = dotenv.env["BASE_URL"]; + final String? _apiKey = dotenv.env["API_KEY"]; + final SecureStorage _secureStorage = SecureStorage(); + + Future> _getHeaders() async { + String? token = await _secureStorage.readSecureData('token'); + + if (token == null || token.isEmpty) { + return { + "Content-type": "application/json; charset=UTF-8", + "Accept": "application/json", + "x-api-key": _apiKey ?? "", + }; + } + + bool isExpired = await isTokenExpired(); + if (isExpired) { + await deleteToken(); + throw Exception('Session expired, please log in again.'); + } + + return { + "Content-type": "application/json; charset=UTF-8", + "Accept": "application/json", + "Authorization": "Bearer $token", + "x-api-key": _apiKey ?? "", + }; + } + + Future _request( + String method, { + required String desturl, + Map headers = const {}, + dynamic body, + Encoding? encoding, + String? baseUrl, + }) async { + final requestHeaders = await _getHeaders(); + String url = "${baseUrl ?? _baseUrl}$desturl"; + debugPrint("url $url"); + + http.Response response; + try { + switch (method.toLowerCase()) { + case 'get': + response = await http.get(Uri.parse(url), headers: requestHeaders); + break; + case 'post': + response = await http.post( + Uri.parse(url), + body: jsonEncode(body), + headers: requestHeaders, + encoding: encoding, + ); + break; + case 'put': + response = await http.put( + Uri.parse(url), + body: jsonEncode(body), + headers: requestHeaders, + encoding: encoding, + ); + break; + case 'delete': + response = await http.delete( + Uri.parse(url), + body: jsonEncode(body), + headers: requestHeaders, + ); + break; + case 'patch': + response = await http.patch( + Uri.parse(url), + body: jsonEncode(body), + headers: requestHeaders, + encoding: encoding, + ); + break; + default: + throw ApiException('Unsupported HTTP method: $method', 405); + } + + final int statusCode = response.statusCode; + if (statusCode < 200 || statusCode >= 400) { + throw ApiException( + 'Error during HTTP $method request: ${response.body}', + statusCode, + ); + } + return jsonDecode(response.body); + } catch (error) { + if (error is ApiException) { + rethrow; + } else { + throw ApiException('Network error: $error', 500); + } + } + } + + Future get( + String desturl, { + Map headers = const {}, + String? baseUrl, + }) async { + return await _request( + 'get', + desturl: desturl, + headers: headers, + baseUrl: baseUrl, + ); + } + + Future post( + String desturl, { + Map headers = const {}, + dynamic body, + Encoding? encoding, + String? baseUrl, + }) async { + return await _request( + 'post', + desturl: desturl, + headers: headers, + body: body, + encoding: encoding, + baseUrl: baseUrl, + ); + } + + Future put( + String desturl, { + Map headers = const {}, + dynamic body, + Encoding? encoding, + String? baseUrl, + }) async { + return await _request( + 'put', + desturl: desturl, + headers: headers, + body: body, + encoding: encoding, + baseUrl: baseUrl, + ); + } + + Future delete( + String desturl, { + Map headers = const {}, + String? baseUrl, + body = const {}, + }) async { + return await _request( + 'delete', + desturl: desturl, + headers: headers, + body: body, + baseUrl: baseUrl, + ); + } + + Future patch( + String destUrl, { + Map headers = const {}, + dynamic body, + Encoding? encoding, + String? baseUrl, + }) async { + return await _request( + 'patch', + desturl: destUrl, + headers: headers, + body: body, + encoding: encoding, + baseUrl: baseUrl, + ); + } +} diff --git a/lib/core/api_services.dart b/lib/core/api_services.dart deleted file mode 100644 index 8bf37d5..0000000 --- a/lib/core/api_services.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; - -class ApiService { - final String baseUrl = dotenv.get('BASE_URL'); - final String apiKey = dotenv.get('API_KEY'); - final FlutterSecureStorage _secureStorage = FlutterSecureStorage(); - - static const Map _headers = { - 'Content-Type': 'application/json', - }; - - Future _getAuthToken() async { - return await _secureStorage.read(key: 'token'); - } - - Future> _getHeaders() async { - final token = await _getAuthToken(); - return { - ..._headers, - 'x-api-key': apiKey, - if (token != null) 'Authorization': 'Bearer $token', - }; - } - - Map _processResponse(http.Response response) { - if (response.body.isEmpty) { - throw Exception('Empty response body'); - } - - try { - final responseJson = jsonDecode(response.body); - - switch (response.statusCode) { - case 200: - return responseJson; - case 400: - throw BadRequestException( - 'Bad request. The server could not process your request.', - ); - case 401: - throw UnauthorizedException( - 'Unauthorized. Please check your credentials.', - ); - case 404: - throw NotFoundException( - 'Not found. The requested resource could not be found.', - ); - case 500: - throw ServerException( - 'Internal server error. Please try again later.', - ); - default: - throw Exception('Failed with status code: ${response.statusCode}'); - } - } catch (e) { - throw Exception('Error parsing response: $e'); - } - } - - Future> get(String endpoint) async { - try { - final headers = await _getHeaders(); - final url = Uri.parse('$baseUrl$endpoint'); - final response = await http.get(url, headers: headers); - - return _processResponse(response); - } catch (e) { - throw NetworkException( - 'Failed to connect to the server. Please check your internet connection.', - ); - } - } - - Future> post( - String endpoint, - Map body, - ) async { - try { - final headers = await _getHeaders(); - final url = Uri.parse('$baseUrl$endpoint'); - - debugPrint('Request URL: $url'); - debugPrint('Request Body: ${jsonEncode(body)}'); - - final response = await http.post( - url, - headers: headers, - body: jsonEncode(body), - ); - - debugPrint('Response: ${response.body}'); - return _processResponse(response); - } catch (e) { - debugPrint('Error during API request: $e'); - throw NetworkException( - 'Failed to connect to the server. Please check your internet connection.', - ); - } - } - - Future> put( - String endpoint, - Map body, - ) async { - try { - final headers = await _getHeaders(); - final url = Uri.parse('$baseUrl$endpoint'); - - debugPrint('Request URL: $url'); - debugPrint('Request Body: ${jsonEncode(body)}'); - - final response = await http.put( - url, - headers: headers, - body: jsonEncode(body), - ); - - debugPrint('Response: ${response.body}'); - return _processResponse(response); - } catch (e) { - debugPrint('Error during API request: $e'); - throw NetworkException( - 'Failed to connect to the server. Please check your internet connection.', - ); - } - } - - Future> patch( - String endpoint, - Map body, - ) async { - try { - final headers = await _getHeaders(); - final url = Uri.parse('$baseUrl$endpoint'); - - debugPrint('Request URL: $url'); - debugPrint('Request Body: ${jsonEncode(body)}'); - - final response = await http.patch( - url, - headers: headers, - body: jsonEncode(body), - ); - - debugPrint('Response: ${response.body}'); - return _processResponse(response); - } catch (e) { - debugPrint('Error during API request: $e'); - throw NetworkException( - 'Failed to connect to the server. Please check your internet connection.', - ); - } - } - - Future> delete(String endpoint) async { - try { - final headers = await _getHeaders(); - final url = Uri.parse('$baseUrl$endpoint'); - final response = await http.delete(url, headers: headers); - - return _processResponse(response); - } catch (e) { - throw NetworkException( - 'Failed to connect to the server. Please check your internet connection.', - ); - } - } -} - -class NetworkException implements Exception { - final String message; - NetworkException(this.message); -} - -class BadRequestException implements Exception { - final String message; - BadRequestException(this.message); -} - -class UnauthorizedException implements Exception { - final String message; - UnauthorizedException(this.message); -} - -class NotFoundException implements Exception { - final String message; - NotFoundException(this.message); -} - -class ServerException implements Exception { - final String message; - ServerException(this.message); -} diff --git a/lib/core/container/injection_container.dart b/lib/core/container/injection_container.dart new file mode 100644 index 0000000..2389e2f --- /dev/null +++ b/lib/core/container/injection_container.dart @@ -0,0 +1,14 @@ +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/otp_vmod.dart'; +import 'package:rijig_mobile/features/auth/repositories/login_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/otp_service.dart'; + +final sl = GetIt.instance; + +void init() { + sl.registerFactory(() => LoginViewModel(LoginService(LoginRepository()))); + sl.registerFactory(() => OtpViewModel(OtpService(OtpRepository()))); +} diff --git a/lib/core/getinfodevice.dart b/lib/core/getinfodevice.dart deleted file mode 100644 index 714ee13..0000000 --- a/lib/core/getinfodevice.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:uuid/uuid.dart'; -import 'dart:io'; - -Future 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; -} diff --git a/lib/core/network/network_info.dart b/lib/core/network/network_info.dart new file mode 100644 index 0000000..995318a --- /dev/null +++ b/lib/core/network/network_info.dart @@ -0,0 +1,21 @@ +import 'dart:io'; +import 'package:connectivity_plus/connectivity_plus.dart'; + +class NetworkInfo { + final Connectivity _connectivity = Connectivity(); + + Future checkConnection() async { + var connectivityResult = await _connectivity.checkConnectivity(); + + if (connectivityResult == ConnectivityResult.none) { + return false; + } + + try { + final lookupResult = await InternetAddress.lookup('google.com'); + return lookupResult.isNotEmpty && lookupResult[0].rawAddress.isNotEmpty; + } catch (_) { + return false; + } + } +} diff --git a/lib/core/router.dart b/lib/core/router.dart index d2d055f..14d85b3 100644 --- a/lib/core/router.dart +++ b/lib/core/router.dart @@ -1,16 +1,4 @@ -import 'package:go_router/go_router.dart'; -import 'package:rijig_mobile/core/navigation.dart'; -import 'package:rijig_mobile/screen/app/activity/activity_screen.dart'; -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'; +import 'package:rijig_mobile/core/utils/exportimportview.dart'; final router = GoRouter( routes: [ @@ -20,22 +8,29 @@ final router = GoRouter( builder: (context, state) => OnboardingPageScreen(), ), GoRoute(path: '/login', builder: (context, state) => LoginScreen()), + + // Rute untuk verifikasi OTP dengan ekstraksi data dari path GoRoute( path: '/verif-otp', builder: (context, state) { - final phone = state.extra as String?; - return VerifotpScreen(phone: phone!); + dynamic phoneNumber = state.extra; + return VerifOtpScreen(phoneNumber: phoneNumber); }, ), - GoRoute(path: '/setpin', builder: (context, state) => SetPinScreen()), - GoRoute(path: '/verifpin', builder: (context, state) => VerifPinScreen()), + + // GoRoute(path: '/setpin', builder: (context, state) => InputPinScreen()), + // GoRoute(path: '/verifpin', builder: (context, state) => VerifPinScreen()), + + // Rute dengan parameter dinamis untuk halaman navigasi GoRoute( path: '/navigasi', builder: (context, state) { - dynamic data = state.extra; + final data = state.extra; return NavigationPage(data: data); }, ), + + // Rute untuk halaman-halaman utama GoRoute(path: '/home', builder: (context, state) => HomeScreen()), GoRoute(path: '/activity', builder: (context, state) => ActivityScreen()), GoRoute( diff --git a/lib/core/storage/expired_token.dart b/lib/core/storage/expired_token.dart new file mode 100644 index 0000000..c170419 --- /dev/null +++ b/lib/core/storage/expired_token.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:jwt_decoder/jwt_decoder.dart'; +import 'package:rijig_mobile/core/storage/secure_storage.dart'; + +final SecureStorage _secureStorage = SecureStorage(); + +Future isTokenExpired() async { + String? token = await _secureStorage.readSecureData('token'); + + if (token == null || token.isEmpty) { + return true; + } + + try { + Map decodedToken = JwtDecoder.decode(token); + int expirationTime = decodedToken['exp']; + int currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + return expirationTime < currentTime; + } catch (e) { + debugPrint("Error decoding token: $e"); + return true; + } +} +// Future isTokenExpired() async { +// String? token = await _secureStorage.readSecureData('token'); + +// if (token == null || token.isEmpty) { +// return true; +// } + +// // Decode the token to get the payload +// Map decodedToken = JwtDecoder.decode(token); + +// // Retrieve the expiration time in seconds +// int expirationTime = decodedToken['exp']; + +// // Get current time in seconds +// int currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; + +// // Check if token is expired +// return expirationTime < currentTime; +// } + + +Future storeSessionData( + String token, + String userId, + String userRole, +) async { + await _secureStorage.writeSecureData('token', token); + await _secureStorage.writeSecureData('user_id', userId); + await _secureStorage.writeSecureData('user_role', userRole); +} + +Future deleteToken() async { + await _secureStorage.deleteSecureData('token'); + await _secureStorage.deleteSecureData('user_id'); + await _secureStorage.deleteSecureData('user_role'); +} diff --git a/lib/core/storage/secure_storage.dart b/lib/core/storage/secure_storage.dart new file mode 100644 index 0000000..dfa3069 --- /dev/null +++ b/lib/core/storage/secure_storage.dart @@ -0,0 +1,17 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class SecureStorage { + final FlutterSecureStorage _storage = FlutterSecureStorage(); + + Future writeSecureData(String key, String value) async { + await _storage.write(key: key, value: value); + } + + Future readSecureData(String key) async { + return await _storage.read(key: key); + } + + Future deleteSecureData(String key) async { + await _storage.delete(key: key); + } +} diff --git a/lib/core/storage/shared_preference.dart b/lib/core/storage/shared_preference.dart new file mode 100644 index 0000000..3997006 --- /dev/null +++ b/lib/core/storage/shared_preference.dart @@ -0,0 +1,13 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class SharedPrefsHelper { + static Future setIsLoggedIn(bool isLoggedIn) async { + final prefs = await SharedPreferences.getInstance(); + prefs.setBool('isLoggedIn', isLoggedIn); + } + + static Future getIsLoggedIn() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool('isLoggedIn') ?? false; + } +} diff --git a/lib/core/utils/exportimportview.dart b/lib/core/utils/exportimportview.dart new file mode 100644 index 0000000..6739d14 --- /dev/null +++ b/lib/core/utils/exportimportview.dart @@ -0,0 +1,13 @@ +export 'package:go_router/go_router.dart'; +export 'package:rijig_mobile/core/utils/navigation.dart'; +export 'package:rijig_mobile/features/activity/activity_screen.dart'; +export 'package:rijig_mobile/features/cart/cart_screen.dart'; +export 'package:rijig_mobile/features/home/home_screen.dart'; +export 'package:rijig_mobile/features/profil/profil_screen.dart'; +export 'package:rijig_mobile/features/requestpick/requestpickup_screen.dart'; +export 'package:rijig_mobile/features/auth/presentation/screen/inputpin_screen.dart'; +export 'package:rijig_mobile/features/auth/presentation/screen/login_screen.dart'; +export 'package:rijig_mobile/features/auth/presentation/screen/otp_screen.dart'; +export 'package:rijig_mobile/features/auth/presentation/screen/verifpin_screen.dart'; +export 'package:rijig_mobile/features/launch/onboardingpage_screen.dart'; +export 'package:rijig_mobile/features/launch/splash_screen.dart'; diff --git a/lib/core/utils/getinfodevice.dart b/lib/core/utils/getinfodevice.dart new file mode 100644 index 0000000..e5c433d --- /dev/null +++ b/lib/core/utils/getinfodevice.dart @@ -0,0 +1,29 @@ +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:uuid/uuid.dart'; +import 'dart:io'; + +Future getDeviceId() async { + final deviceInfo = DeviceInfoPlugin(); + String deviceID = ''; + + try { + 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(); + } + } catch (e) { + debugPrint("Error fetching device ID: $e"); + + var uuid = Uuid(); + deviceID = uuid.v4(); + } + + return deviceID; +} diff --git a/lib/core/guide.dart b/lib/core/utils/guide.dart similarity index 100% rename from lib/core/guide.dart rename to lib/core/utils/guide.dart diff --git a/lib/core/navigation.dart b/lib/core/utils/navigation.dart similarity index 93% rename from lib/core/navigation.dart rename to lib/core/utils/navigation.dart index 3ec9664..f528869 100644 --- a/lib/core/navigation.dart +++ b/lib/core/utils/navigation.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:iconsax_flutter/iconsax_flutter.dart'; -import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/core/router.dart'; -import 'package:rijig_mobile/screen/app/activity/activity_screen.dart'; -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/features/activity/activity_screen.dart'; +import 'package:rijig_mobile/features/cart/cart_screen.dart'; +import 'package:rijig_mobile/features/home/home_screen.dart'; +import 'package:rijig_mobile/features/profil/profil_screen.dart'; import 'package:shared_preferences/shared_preferences.dart'; class NavigationPage extends StatefulWidget { diff --git a/lib/core/utils/validation.dart b/lib/core/utils/validation.dart new file mode 100644 index 0000000..0350ead --- /dev/null +++ b/lib/core/utils/validation.dart @@ -0,0 +1,13 @@ +class Validation { + static bool isValidPhoneNumber(String phone) { + final RegExp phoneRegExp = RegExp(r"^\+?1?\d{9,15}$"); + return phoneRegExp.hasMatch(phone); + } + + static bool isValidEmail(String email) { + final RegExp emailRegExp = RegExp( + r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$", + ); + return emailRegExp.hasMatch(email); + } +} diff --git a/lib/screen/app/activity/activity_screen.dart b/lib/features/activity/activity_screen.dart similarity index 100% rename from lib/screen/app/activity/activity_screen.dart rename to lib/features/activity/activity_screen.dart diff --git a/lib/features/auth/model/login_model.dart b/lib/features/auth/model/login_model.dart new file mode 100644 index 0000000..b517acb --- /dev/null +++ b/lib/features/auth/model/login_model.dart @@ -0,0 +1,19 @@ +class LoginModel { + final String phone; + + LoginModel({required this.phone}); + + Map toJson() { + return {'phone': phone}; + } +} + +class LoginResponse { + final String message; + + LoginResponse({required this.message}); + + factory LoginResponse.fromJson(Map json) { + return LoginResponse(message: json['meta']['message']); + } +} diff --git a/lib/features/auth/model/otp_model.dart b/lib/features/auth/model/otp_model.dart new file mode 100644 index 0000000..d16d541 --- /dev/null +++ b/lib/features/auth/model/otp_model.dart @@ -0,0 +1,39 @@ +class OtpModel { + final String phone; + final String otp; + final String deviceId; + + OtpModel({required this.phone, required this.otp, required this.deviceId}); + + factory OtpModel.fromJson(Map json) { + return OtpModel( + phone: json['phone'], + otp: json['otp'], + deviceId: json['device_id'], + ); + } + + Map toJson() { + return {'phone': phone, 'otp': otp, 'device_id': deviceId}; + } +} + +class VerifOkResponse { + final String userId; + final String userRole; + final String token; + + VerifOkResponse({ + required this.userId, + required this.userRole, + required this.token, + }); + + factory VerifOkResponse.fromJson(Map json) { + return VerifOkResponse( + userId: json['data']['user_id'], + userRole: json['data']['user_role'], + token: json['data']['token'], + ); + } +} diff --git a/lib/features/auth/model/response_model.dart b/lib/features/auth/model/response_model.dart new file mode 100644 index 0000000..a6d1ab7 --- /dev/null +++ b/lib/features/auth/model/response_model.dart @@ -0,0 +1,33 @@ +class ResponseModel { + final bool status; + final String message; + + ResponseModel({required this.status, required this.message}); + + factory ResponseModel.fromJson(Map json) { + return ResponseModel( + status: json['meta']['status'] == 200, + message: json['meta']['message'], + ); + } +} + +class UserModel { + final String userId; + final String userRole; + final String token; + + UserModel({ + required this.userId, + required this.userRole, + required this.token, + }); + + factory UserModel.fromJson(Map json) { + return UserModel( + userId: json['data']['user_id'], + userRole: json['data']['user_role'], + token: json['data']['token'], + ); + } +} diff --git a/lib/features/auth/model/user_model.dart b/lib/features/auth/model/user_model.dart new file mode 100644 index 0000000..f0e78e1 --- /dev/null +++ b/lib/features/auth/model/user_model.dart @@ -0,0 +1,15 @@ +class UserModel { + final String userId; + final String userRole; + final String token; + + UserModel({required this.userId, required this.userRole, required this.token}); + + factory UserModel.fromJson(Map json) { + return UserModel( + userId: json['data']['user_id'], + userRole: json['data']['user_role'], + token: json['data']['token'], + ); + } +} diff --git a/lib/features/auth/model/userpin_model.dart b/lib/features/auth/model/userpin_model.dart new file mode 100644 index 0000000..8a18093 --- /dev/null +++ b/lib/features/auth/model/userpin_model.dart @@ -0,0 +1,33 @@ +// import 'package:rijig_mobile/core/api/api_services.dart'; +// import 'package:rijig_mobile/features/auth/model/response_model.dart'; + +// class PinModel { +// final ApiService _apiService = ApiService(); + +// Future checkPinStatus(String userId) async { +// try { +// var response = await _apiService.get('/cek-pin-status'); +// return ResponseModel.fromJson(response); +// } catch (e) { +// rethrow; +// } +// } + +// Future setPin(String pin) async { +// try { +// var response = await _apiService.post('/set-pin', {'userpin': pin}); +// return ResponseModel.fromJson(response); +// } catch (e) { +// rethrow; +// } +// } + +// Future verifyPin(String pin) async { +// try { +// var response = await _apiService.post('/verif-pin', {'userpin': pin}); +// return ResponseModel.fromJson(response); +// } catch (e) { +// rethrow; +// } +// } +// } diff --git a/lib/features/auth/presentation/screen/inputpin_screen.dart b/lib/features/auth/presentation/screen/inputpin_screen.dart new file mode 100644 index 0000000..310807f --- /dev/null +++ b/lib/features/auth/presentation/screen/inputpin_screen.dart @@ -0,0 +1,56 @@ +// import 'package:flutter/material.dart'; +// import 'package:provider/provider.dart'; +// import 'package:rijig_mobile/core/router.dart'; +// import 'package:rijig_mobile/features/auth/presentation/viewmodel/userpin_vmod.dart'; + +// class InputPinScreen extends StatefulWidget { +// const InputPinScreen({super.key}); + +// @override +// InputPinScreenState createState() => InputPinScreenState(); +// } + +// class InputPinScreenState extends State { +// final _pinController = TextEditingController(); + +// @override +// Widget build(BuildContext context) { +// final pinViewModel = Provider.of(context); + +// return Scaffold( +// appBar: AppBar(title: Text("Buat PIN Baru")), +// body: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text("Buat PIN Anda (6 digit)", style: TextStyle(fontSize: 18)), +// SizedBox(height: 20), +// TextField( +// controller: _pinController, +// decoration: InputDecoration(labelText: "PIN"), +// keyboardType: TextInputType.number, +// obscureText: true, +// ), +// SizedBox(height: 20), +// ElevatedButton( +// onPressed: () async { +// String pin = _pinController.text; + +// await pinViewModel.createPin(pin); +// if (pinViewModel.pinExists == true) { +// router.go('/navigasi'); +// } else { +// ScaffoldMessenger.of( +// context, +// ).showSnackBar(SnackBar(content: Text('Gagal membuat PIN'))); +// } +// }, +// child: Text("Buat PIN"), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/features/auth/presentation/screen/login_screen.dart b/lib/features/auth/presentation/screen/login_screen.dart new file mode 100644 index 0000000..6497e69 --- /dev/null +++ b/lib/features/auth/presentation/screen/login_screen.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:rijig_mobile/core/router.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/login_vmod.dart'; +import 'package:rijig_mobile/widget/buttoncard.dart'; +import 'package:rijig_mobile/widget/formfiled.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + LoginScreenState createState() => LoginScreenState(); +} + +class LoginScreenState extends State { + final TextEditingController _phoneController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text("Login")), + body: Consumer( + builder: (context, viewModel, child) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FormFieldOne( + controllers: _phoneController, + hintText: 'Phone Number', + isRequired: true, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.phone, + onTap: () {}, + onChanged: (value) {}, + fontSize: 14, + fontSizeField: 16, + onFieldSubmitted: (value) {}, + readOnly: false, + enabled: true, + ), + SizedBox(height: 20), + CardButtonOne( + textButton: + viewModel.isLoading ? 'Sending OTP...' : 'Send OTP', + fontSized: 16, + colorText: whiteColor, + borderRadius: 10, + horizontal: double.infinity, + vertical: 50, + onTap: () async { + if (_phoneController.text.isNotEmpty) { + debugPrint("send otp dipencet"); + await viewModel.loginOrRegister(_phoneController.text); + if (viewModel.loginResponse != null) { + router.go("/verif-otp", extra: _phoneController.text); + } + } + }, + loadingTrue: viewModel.isLoading, + usingRow: false, + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/auth/presentation/screen/otp_screen.dart b/lib/features/auth/presentation/screen/otp_screen.dart new file mode 100644 index 0000000..9c916b6 --- /dev/null +++ b/lib/features/auth/presentation/screen/otp_screen.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:pin_code_fields/pin_code_fields.dart'; +import 'package:provider/provider.dart'; +import 'package:rijig_mobile/core/router.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/otp_vmod.dart'; +import 'package:rijig_mobile/widget/buttoncard.dart'; + +class VerifOtpScreen extends StatefulWidget { + final String phoneNumber; + + const VerifOtpScreen({super.key, required this.phoneNumber}); + + @override + VerifOtpScreenState createState() => VerifOtpScreenState(); +} + +class VerifOtpScreenState extends State { + final TextEditingController _otpController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text("Verify OTP")), + body: Consumer( + builder: (context, viewModel, child) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Text("OTP has been sent to ${widget.phoneNumber}"), + SizedBox(height: 20), + + PinCodeTextField( + controller: _otpController, + appContext: context, + length: 4, + obscureText: false, + animationType: AnimationType.fade, + pinTheme: PinTheme( + shape: PinCodeFieldShape.box, + borderRadius: BorderRadius.circular(5), + fieldHeight: 50, + fieldWidth: 50, + activeFillColor: Colors.white, + inactiveFillColor: Colors.white, + selectedFillColor: Colors.white, + activeColor: Colors.black, + inactiveColor: Colors.black, + selectedColor: Colors.blue, + ), + onChanged: (value) {}, + onCompleted: (value) {}, + ), + SizedBox(height: 20), + + CardButtonOne( + textButton: + viewModel.isLoading ? 'Verifying OTP...' : 'Verify OTP', + fontSized: 16, + colorText: Colors.white, + borderRadius: 10, + horizontal: double.infinity, + vertical: 50, + onTap: () async { + if (_otpController.text.isNotEmpty) { + await viewModel.verifyOtp( + widget.phoneNumber, + _otpController.text, + ); + if (viewModel.authResponse != null) { + router.go("/navigasi"); + } + } + }, + loadingTrue: viewModel.isLoading, + usingRow: false, + ), + + if (viewModel.errorMessage != null) + Text( + viewModel.errorMessage!, + style: TextStyle(color: Colors.red), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/auth/presentation/screen/verifpin_screen.dart b/lib/features/auth/presentation/screen/verifpin_screen.dart new file mode 100644 index 0000000..6227483 --- /dev/null +++ b/lib/features/auth/presentation/screen/verifpin_screen.dart @@ -0,0 +1,59 @@ +// import 'package:flutter/material.dart'; +// import 'package:provider/provider.dart'; +// import 'package:rijig_mobile/core/router.dart'; +// import 'package:rijig_mobile/features/auth/presentation/viewmodel/userpin_vmod.dart'; + +// class VerifPinScreen extends StatefulWidget { +// const VerifPinScreen({super.key}); + +// @override +// VerifPinScreenState createState() => VerifPinScreenState(); +// } + +// class VerifPinScreenState extends State { +// final _pinController = TextEditingController(); + +// @override +// Widget build(BuildContext context) { +// final pinViewModel = Provider.of(context); + +// return Scaffold( +// appBar: AppBar(title: Text("Verifikasi PIN")), +// body: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// "Masukkan PIN yang sudah dibuat", +// style: TextStyle(fontSize: 18), +// ), +// SizedBox(height: 20), +// TextField( +// controller: _pinController, +// decoration: InputDecoration(labelText: "PIN"), +// keyboardType: TextInputType.number, +// obscureText: true, +// ), +// SizedBox(height: 20), +// ElevatedButton( +// onPressed: () async { +// String pin = _pinController.text; + +// await pinViewModel.verifyPin(pin); +// if (pinViewModel.pinExists == true) { +// router.go('/navigasi'); +// } else { +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar(content: Text('PIN yang anda masukkan salah')), +// ); +// } +// }, +// child: Text("Verifikasi PIN"), +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/features/auth/presentation/viewmodel/login_vmod.dart b/lib/features/auth/presentation/viewmodel/login_vmod.dart new file mode 100644 index 0000000..93defe2 --- /dev/null +++ b/lib/features/auth/presentation/viewmodel/login_vmod.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/features/auth/model/login_model.dart'; +import 'package:rijig_mobile/features/auth/service/login_service.dart'; + +class LoginViewModel extends ChangeNotifier { + final LoginService _loginService; + + LoginViewModel(this._loginService); + + bool isLoading = false; + String? errorMessage; + LoginResponse? loginResponse; + + Future loginOrRegister(String phone) async { + isLoading = true; + errorMessage = null; + notifyListeners(); + + try { + loginResponse = await _loginService.loginOrRegister(phone); + } catch (e) { + errorMessage = "Error: ${e.toString()}"; + } + + isLoading = false; + notifyListeners(); + } +} diff --git a/lib/features/auth/presentation/viewmodel/otp_vmod.dart b/lib/features/auth/presentation/viewmodel/otp_vmod.dart new file mode 100644 index 0000000..a0a6d60 --- /dev/null +++ b/lib/features/auth/presentation/viewmodel/otp_vmod.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/features/auth/model/otp_model.dart'; +import 'package:rijig_mobile/features/auth/service/otp_service.dart'; + +class OtpViewModel extends ChangeNotifier { + final OtpService _otpService; + + OtpViewModel(this._otpService); + + bool isLoading = false; + String? errorMessage; + VerifOkResponse? authResponse; + + Future verifyOtp(String phone, String otp) async { + isLoading = true; + errorMessage = null; + notifyListeners(); + + try { + String deviceId = await _otpService.getDeviceInfo(); + + OtpModel otpModel = OtpModel(phone: phone, otp: otp, deviceId: deviceId); + + authResponse = await _otpService.verifyOtp(otpModel); + + if (authResponse != null) { + await _otpService.storeSessionData( + authResponse!.token, + authResponse!.userId, + authResponse!.userRole, + ); + } + } catch (e) { + errorMessage = "Error: ${e.toString()}"; + } + + isLoading = false; + notifyListeners(); + } +} diff --git a/lib/viewmodel/user_vmod.dart b/lib/features/auth/presentation/viewmodel/user_vmod.dart similarity index 100% rename from lib/viewmodel/user_vmod.dart rename to lib/features/auth/presentation/viewmodel/user_vmod.dart diff --git a/lib/features/auth/presentation/viewmodel/userpin_vmod.dart b/lib/features/auth/presentation/viewmodel/userpin_vmod.dart new file mode 100644 index 0000000..f1c4e8f --- /dev/null +++ b/lib/features/auth/presentation/viewmodel/userpin_vmod.dart @@ -0,0 +1,58 @@ +// import 'package:flutter/material.dart'; +// import 'package:rijig_mobile/features/auth/model/userpin_model.dart'; + +// class PinViewModel extends ChangeNotifier { +// final PinModel _pinModel = PinModel(); +// bool? pinExists; + +// Future checkPinStatus(String userId) async { +// try { +// var response = await _pinModel.checkPinStatus(userId); + +// if (response?.status == 200) { +// pinExists = true; +// } else { +// pinExists = false; +// } +// notifyListeners(); +// } catch (e) { +// debugPrint('Error checking pin status: $e'); +// pinExists = false; +// notifyListeners(); +// } +// } + +// Future createPin(String pin) async { +// try { +// var response = await _pinModel.setPin(pin); + +// if (response?.status == 201) { +// pinExists = true; +// } else { +// pinExists = false; +// } +// notifyListeners(); +// } catch (e) { +// debugPrint('Error creating pin: $e'); +// pinExists = false; +// notifyListeners(); +// } +// } + +// Future verifyPin(String pin) async { +// try { +// var response = await _pinModel.verifyPin(pin); + +// if (response?.status == 200) { +// pinExists = true; +// } else { +// pinExists = false; +// } +// notifyListeners(); +// } catch (e) { +// debugPrint('Error verifying pin: $e'); +// pinExists = false; +// notifyListeners(); +// } +// } +// } diff --git a/lib/features/auth/repositories/login_repository.dart b/lib/features/auth/repositories/login_repository.dart new file mode 100644 index 0000000..fc307df --- /dev/null +++ b/lib/features/auth/repositories/login_repository.dart @@ -0,0 +1,14 @@ +import 'package:rijig_mobile/core/api/api_services.dart'; +import 'package:rijig_mobile/features/auth/model/login_model.dart'; + +class LoginRepository { + final Https _https = Https(); + + Future loginOrRegister(String phone) async { + final response = await _https.post( + '/authmasyarakat/auth', + body: {'phone': phone}, + ); + return LoginResponse.fromJson(response); + } +} diff --git a/lib/features/auth/repositories/otp_repository.dart b/lib/features/auth/repositories/otp_repository.dart new file mode 100644 index 0000000..2b74316 --- /dev/null +++ b/lib/features/auth/repositories/otp_repository.dart @@ -0,0 +1,14 @@ +import 'package:rijig_mobile/core/api/api_services.dart'; +import 'package:rijig_mobile/features/auth/model/otp_model.dart'; + +class OtpRepository { + final Https _https = Https(); + + Future verifyOtp(OtpModel otpModel) async { + final response = await _https.post( + '/authmasyarakat/verify-otp', + body: otpModel.toJson(), + ); + return VerifOkResponse.fromJson(response); + } +} diff --git a/lib/features/auth/service/login_service.dart b/lib/features/auth/service/login_service.dart new file mode 100644 index 0000000..8867061 --- /dev/null +++ b/lib/features/auth/service/login_service.dart @@ -0,0 +1,16 @@ +import 'package:rijig_mobile/features/auth/model/login_model.dart'; +import 'package:rijig_mobile/features/auth/repositories/login_repository.dart'; + +class LoginService { + final LoginRepository _loginRepository; + + LoginService(this._loginRepository); + + Future loginOrRegister(String phone) async { + try { + return await _loginRepository.loginOrRegister(phone); + } catch (e) { + throw Exception('Login failed: $e'); + } + } +} diff --git a/lib/features/auth/service/otp_service.dart b/lib/features/auth/service/otp_service.dart new file mode 100644 index 0000000..7d39ed2 --- /dev/null +++ b/lib/features/auth/service/otp_service.dart @@ -0,0 +1,33 @@ +import 'package:rijig_mobile/core/storage/secure_storage.dart'; +import 'package:rijig_mobile/core/utils/getinfodevice.dart'; +import 'package:rijig_mobile/features/auth/model/otp_model.dart'; +import 'package:rijig_mobile/features/auth/repositories/otp_repository.dart'; + +class OtpService { + final OtpRepository _otpRepository; + // final SecureStorage _secureStorage = SecureStorage(); + + OtpService(this._otpRepository); + + Future getDeviceInfo() async { + return await getDeviceId(); + } + + Future storeSessionData( + String token, + String userId, + String userRole, + ) async { + await SecureStorage().writeSecureData('token', token); + await SecureStorage().writeSecureData('user_id', userId); + await SecureStorage().writeSecureData('user_role', userRole); + } + + Future verifyOtp(OtpModel otpModel) async { + try { + return await _otpRepository.verifyOtp(otpModel); + } catch (e) { + throw Exception('OTP Verification failed: $e'); + } + } +} diff --git a/lib/screen/app/cart/cart_screen.dart b/lib/features/cart/cart_screen.dart similarity index 100% rename from lib/screen/app/cart/cart_screen.dart rename to lib/features/cart/cart_screen.dart diff --git a/lib/screen/app/home/components/categories.dart b/lib/features/home/components/categories.dart similarity index 100% rename from lib/screen/app/home/components/categories.dart rename to lib/features/home/components/categories.dart diff --git a/lib/screen/app/home/components/discount_banner.dart b/lib/features/home/components/discount_banner.dart similarity index 100% rename from lib/screen/app/home/components/discount_banner.dart rename to lib/features/home/components/discount_banner.dart diff --git a/lib/screen/app/home/components/home_header.dart b/lib/features/home/components/home_header.dart similarity index 100% rename from lib/screen/app/home/components/home_header.dart rename to lib/features/home/components/home_header.dart diff --git a/lib/screen/app/home/components/icon_btn_with_counter.dart b/lib/features/home/components/icon_btn_with_counter.dart similarity index 100% rename from lib/screen/app/home/components/icon_btn_with_counter.dart rename to lib/features/home/components/icon_btn_with_counter.dart diff --git a/lib/screen/app/home/components/popular_product.dart b/lib/features/home/components/popular_product.dart similarity index 91% rename from lib/screen/app/home/components/popular_product.dart rename to lib/features/home/components/popular_product.dart index a2dfca2..15c30de 100644 --- a/lib/screen/app/home/components/popular_product.dart +++ b/lib/features/home/components/popular_product.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:rijig_mobile/model/product.dart'; -import 'package:rijig_mobile/screen/app/home/components/product_card.dart'; +import 'package:rijig_mobile/features/home/model/product.dart'; +import 'package:rijig_mobile/features/home/components/product_card.dart'; class PopularProducts extends StatelessWidget { const PopularProducts({super.key}); diff --git a/lib/screen/app/home/components/product_card.dart b/lib/features/home/components/product_card.dart similarity index 95% rename from lib/screen/app/home/components/product_card.dart rename to lib/features/home/components/product_card.dart index d9d9d7e..30f6da5 100644 --- a/lib/screen/app/home/components/product_card.dart +++ b/lib/features/home/components/product_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:rijig_mobile/core/guide.dart'; -import 'package:rijig_mobile/model/product.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; +import 'package:rijig_mobile/features/home/model/product.dart'; class ProductCard extends StatelessWidget { const ProductCard({ diff --git a/lib/screen/app/home/components/search_field.dart b/lib/features/home/components/search_field.dart similarity index 100% rename from lib/screen/app/home/components/search_field.dart rename to lib/features/home/components/search_field.dart diff --git a/lib/screen/app/home/components/special_offers.dart b/lib/features/home/components/special_offers.dart similarity index 100% rename from lib/screen/app/home/components/special_offers.dart rename to lib/features/home/components/special_offers.dart diff --git a/lib/screen/app/home/home_screen.dart b/lib/features/home/home_screen.dart similarity index 64% rename from lib/screen/app/home/home_screen.dart rename to lib/features/home/home_screen.dart index 3c318b6..88e22dc 100644 --- a/lib/screen/app/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:rijig_mobile/screen/app/home/components/categories.dart'; -import 'package:rijig_mobile/screen/app/home/components/discount_banner.dart'; -import 'package:rijig_mobile/screen/app/home/components/home_header.dart'; -import 'package:rijig_mobile/screen/app/home/components/popular_product.dart'; -import 'package:rijig_mobile/screen/app/home/components/special_offers.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'; +import 'package:rijig_mobile/features/home/components/popular_product.dart'; +import 'package:rijig_mobile/features/home/components/special_offers.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -15,7 +15,6 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { @override Widget build(BuildContext context) { - // final titleofscreen = "Home"; return const Scaffold( body: SafeArea( child: SingleChildScrollView( diff --git a/lib/model/product.dart b/lib/features/home/model/product.dart similarity index 100% rename from lib/model/product.dart rename to lib/features/home/model/product.dart diff --git a/lib/screen/launch/onboardingpage_screen.dart b/lib/features/launch/onboardingpage_screen.dart similarity index 98% rename from lib/screen/launch/onboardingpage_screen.dart rename to lib/features/launch/onboardingpage_screen.dart index e7b55b3..fd08dae 100644 --- a/lib/screen/launch/onboardingpage_screen.dart +++ b/lib/features/launch/onboardingpage_screen.dart @@ -1,6 +1,6 @@ import 'package:concentric_transition/concentric_transition.dart'; import 'package:flutter/material.dart'; -import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/core/router.dart'; final pages = [ diff --git a/lib/features/launch/splash_screen.dart b/lib/features/launch/splash_screen.dart new file mode 100644 index 0000000..b35fb8f --- /dev/null +++ b/lib/features/launch/splash_screen.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/core/network/network_info.dart'; +import 'package:rijig_mobile/core/router.dart'; +import 'package:rijig_mobile/core/storage/expired_token.dart'; +// import 'package:rijig_mobile/core/storage/secure_storage.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + SplashScreenState createState() => SplashScreenState(); +} + +class SplashScreenState extends State { + bool _isCheckingConnection = true; + + @override + void initState() { + super.initState(); + _checkNetworkConnection(); + _checkLoginStatus(); + } + + Future _checkNetworkConnection() async { + bool isConnected = await NetworkInfo().checkConnection(); + + setState(() { + _isCheckingConnection = false; + }); + + if (!isConnected) { + _showNoInternetDialog(); + return; + } + } + + Future _checkLoginStatus() async { + bool expired = await isTokenExpired(); + if (expired) { + debugPrint("tets expired"); + router.go("/onboarding"); + } else { + debugPrint("test not expired"); + router.go("/navigasi"); + } + } + + void _showNoInternetDialog() { + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text('No Internet'), + content: Text('Mohon periksa koneksi internet anda, dan coba lagi'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('OK'), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Stack( + children: [ + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Image.asset('assets/image/Go_Ride.png', height: 200), + ), + Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.only(bottom: 250.0), + child: Text( + 'Rijig', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Colors.blue, + fontFamily: 'Roboto', + ), + ), + ), + ), + + if (_isCheckingConnection) + Align( + alignment: Alignment.center, + child: CircularProgressIndicator(), + ), + ], + ), + ); + } +} diff --git a/lib/screen/app/profil/profil_screen.dart b/lib/features/profil/profil_screen.dart similarity index 100% rename from lib/screen/app/profil/profil_screen.dart rename to lib/features/profil/profil_screen.dart diff --git a/lib/screen/app/requestpick/requestpickup_screen.dart b/lib/features/requestpick/requestpickup_screen.dart similarity index 100% rename from lib/screen/app/requestpick/requestpickup_screen.dart rename to lib/features/requestpick/requestpickup_screen.dart diff --git a/lib/main.dart b/lib/main.dart index 585c7f6..fa16e35 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,15 +5,17 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:intl/date_symbol_data_local.dart'; 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/viewmodel/auth_vmod.dart'; -import 'package:rijig_mobile/viewmodel/userpin_vmod.dart'; // Import PinViewModel +import 'package:rijig_mobile/features/auth/presentation/viewmodel/login_vmod.dart'; +import 'package:rijig_mobile/features/auth/presentation/viewmodel/otp_vmod.dart'; void main() async { await dotenv.load(fileName: "server/.env.dev"); HttpOverrides.global = MyHttpOverrides(); WidgetsFlutterBinding.ensureInitialized(); await initializeDateFormatting('id_ID', null).then((_) { + init(); runApp(const MyApp()); }); } @@ -23,19 +25,19 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (_) => AuthViewModel(), - child: ChangeNotifierProvider( - create: (_) => PinViewModel(), // Tambahkan PinViewModel - child: ScreenUtilInit( - designSize: const Size(375, 812), - builder: (_, child) { - return MaterialApp.router( - debugShowCheckedModeBanner: false, - routerConfig: router, - ); - }, - ), + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => sl()), + ChangeNotifierProvider(create: (_) => sl()), + ], + child: ScreenUtilInit( + designSize: const Size(375, 812), + builder: (_, child) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + routerConfig: router, + ); + }, ), ); } diff --git a/lib/model/auth_model.dart b/lib/model/auth_model.dart deleted file mode 100644 index 5769508..0000000 --- a/lib/model/auth_model.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:rijig_mobile/core/api_services.dart'; -import 'package:rijig_mobile/model/response_model.dart'; - -class AuthModel { - final ApiService _apiService = ApiService(); - - Future login(String phone) async { - try { - var response = await _apiService.post('/authmasyarakat/auth', { - 'phone': phone, - }); - return ResponseModel.fromJson(response); - } catch (e) { - rethrow; - } - } - - Future 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) { - rethrow; - } - } -} diff --git a/lib/model/response_model.dart b/lib/model/response_model.dart deleted file mode 100644 index 334a489..0000000 --- a/lib/model/response_model.dart +++ /dev/null @@ -1,15 +0,0 @@ -class ResponseModel { - final int status; - final String message; - final Map? data; - - ResponseModel({required this.status, required this.message, this.data}); - - factory ResponseModel.fromJson(Map json) { - return ResponseModel( - status: json['meta']?['status'] ?? 0, - message: json['meta']?['message'] ?? '', - data: json['data'], - ); - } -} diff --git a/lib/model/user_model.dart b/lib/model/user_model.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/model/userpin_model.dart b/lib/model/userpin_model.dart deleted file mode 100644 index f654aec..0000000 --- a/lib/model/userpin_model.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:rijig_mobile/core/api_services.dart'; -import 'package:rijig_mobile/model/response_model.dart'; - -class PinModel { - final ApiService _apiService = ApiService(); - - Future checkPinStatus(String userId) async { - try { - var response = await _apiService.get('/cek-pin-status'); - return ResponseModel.fromJson(response); - } catch (e) { - rethrow; - } - } - - Future setPin(String pin) async { - try { - var response = await _apiService.post('/set-pin', {'userpin': pin}); - return ResponseModel.fromJson(response); - } catch (e) { - rethrow; - } - } - - Future verifyPin(String pin) async { - try { - var response = await _apiService.post('/verif-pin', {'userpin': pin}); - return ResponseModel.fromJson(response); - } catch (e) { - rethrow; - } - } -} diff --git a/lib/screen/auth/inputpin_screen.dart b/lib/screen/auth/inputpin_screen.dart deleted file mode 100644 index 0d2f792..0000000 --- a/lib/screen/auth/inputpin_screen.dart +++ /dev/null @@ -1,56 +0,0 @@ -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 SetPinScreen extends StatefulWidget { - const SetPinScreen({super.key}); - - @override - SetPinScreenState createState() => SetPinScreenState(); -} - -class SetPinScreenState extends State { - final _pinController = TextEditingController(); - - @override - Widget build(BuildContext context) { - final pinViewModel = Provider.of(context); - - return Scaffold( - appBar: AppBar(title: Text("Buat PIN Baru")), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Buat PIN Anda (6 digit)", style: TextStyle(fontSize: 18)), - SizedBox(height: 20), - TextField( - controller: _pinController, - decoration: InputDecoration(labelText: "PIN"), - keyboardType: TextInputType.number, - obscureText: true, - ), - SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - String pin = _pinController.text; - - await pinViewModel.createPin(pin); - if (pinViewModel.pinExists == true) { - router.go('/navigasi'); - } else { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('Gagal membuat PIN'))); - } - }, - child: Text("Buat PIN"), - ), - ], - ), - ), - ); - } -} diff --git a/lib/screen/auth/login_screen.dart b/lib/screen/auth/login_screen.dart deleted file mode 100644 index a3cffb4..0000000 --- a/lib/screen/auth/login_screen.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:flutter/material.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/widget/buttoncard.dart'; -import 'package:rijig_mobile/widget/formfiled.dart'; - -class LoginScreen extends StatefulWidget { - const LoginScreen({super.key}); - - @override - LoginScreenState createState() => LoginScreenState(); -} - -class LoginScreenState extends State { - final _phoneController = TextEditingController(); - - @override - void dispose() { - _phoneController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30), - child: Consumer( - builder: (context, userVM, child) { - debugPrint('AuthModel Status: ${userVM.authModel?.status}'); - - if (userVM.authModel?.status == 200 && !userVM.isLoading) { - debugPrint( - 'OTP Sent Successfully. Navigating to Verif-OTP Screen.', - ); - - WidgetsBinding.instance.addPostFrameCallback((_) { - router.go('/verif-otp', extra: _phoneController.text); - }); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FormFieldOne( - controllers: _phoneController, - hintText: 'Phone Number', - isRequired: true, - onTap: () {}, - keyboardType: TextInputType.phone, - errorText: userVM.errorMessage, - ), - const SizedBox(height: 20), - - userVM.isLoading - ? CardButtonOne( - textButton: 'Sending OTP...', - fontSized: 16, - colorText: Colors.white, - borderRadius: 12, - horizontal: double.infinity, - vertical: 50, - onTap: () {}, - loadingTrue: true, - usingRow: false, - ) - : CardButtonOne( - textButton: 'Send OTP', - fontSized: 16, - colorText: Colors.white, - borderRadius: 12, - horizontal: double.infinity, - vertical: 50, - onTap: () { - if (_phoneController.text.isNotEmpty) { - debugPrint( - 'Sending OTP to: ${_phoneController.text}', - ); - userVM.login(_phoneController.text); - } - }, - loadingTrue: false, - usingRow: false, - ), - - if (userVM.authModel != null) - Padding( - padding: const EdgeInsets.only(top: 20), - child: Text( - userVM.authModel!.message, - style: TextStyle( - color: - userVM.authModel!.status == 200 - ? Colors.green - : Colors.red, - ), - ), - ), - ], - ); - }, - ), - ), - ), - ); - } -} diff --git a/lib/screen/auth/otp_screen.dart b/lib/screen/auth/otp_screen.dart deleted file mode 100644 index fcf75df..0000000 --- a/lib/screen/auth/otp_screen.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:pin_code_fields/pin_code_fields.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 'package:rijig_mobile/widget/buttoncard.dart'; - -class VerifotpScreen extends StatefulWidget { - final dynamic phone; - - const VerifotpScreen({super.key, required this.phone}); - - @override - State createState() => _VerifotpScreenState(); -} - -class _VerifotpScreenState extends State { - final _otpController = TextEditingController(); - - @override - Widget build(BuildContext context) { - - final authViewModel = Provider.of(context); - final pinViewModel = Provider.of(context); - return Scaffold( - body: SafeArea( - child: Padding( - padding: EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Phone: ${widget.phone}', style: TextStyle(fontSize: 18)), - SizedBox(height: 20), - - PinCodeTextField( - controller: _otpController, - length: 4, - onChanged: (value) {}, - appContext: context, - keyboardType: TextInputType.number, - autoFocus: true, - pinTheme: PinTheme( - shape: PinCodeFieldShape.box, - borderRadius: BorderRadius.circular(5), - fieldHeight: 50, - fieldWidth: 50, - inactiveColor: Colors.black54, - activeColor: Colors.blue, - selectedColor: Colors.blue, - ), - animationType: AnimationType.fade, - textStyle: TextStyle(fontSize: 20, color: Colors.black), - ), - SizedBox(height: 20), - CardButtonOne( - textButton: 'Verify OTP', - fontSized: 16, - colorText: Colors.white, - borderRadius: 12, - horizontal: double.infinity, - vertical: 50, - onTap: () { - - - - - - - - - - - - authViewModel.verifyOtp(widget.phone, _otpController.text).then(( - _, - ) { - - pinViewModel - .checkPinStatus( - authViewModel.authModel?.data?['user_id'], - ) - .then((_) { - if (pinViewModel.pinExists == false) { - router.go('/setpin'); - } else if (pinViewModel.pinExists == true) { - router.go('/verifpin'); - } else { - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Gagal memverifikasi status PIN'), - ), - ); - } - }); - }); - }, - loadingTrue: authViewModel.isLoading, - usingRow: false, - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/screen/auth/verifpin_screen.dart b/lib/screen/auth/verifpin_screen.dart deleted file mode 100644 index 872bc38..0000000 --- a/lib/screen/auth/verifpin_screen.dart +++ /dev/null @@ -1,60 +0,0 @@ -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 - VerifPinScreenState createState() => VerifPinScreenState(); -} - -class VerifPinScreenState extends State { - final _pinController = TextEditingController(); - - @override - Widget build(BuildContext context) { - final pinViewModel = Provider.of(context); - - return Scaffold( - appBar: AppBar(title: Text("Verifikasi PIN")), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Masukkan PIN yang sudah dibuat", - style: TextStyle(fontSize: 18), - ), - SizedBox(height: 20), - TextField( - controller: _pinController, - decoration: InputDecoration(labelText: "PIN"), - keyboardType: TextInputType.number, - obscureText: true, - ), - SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - // String userId = 'user_id_here'; - String pin = _pinController.text; - - await pinViewModel.verifyPin(pin); - if (pinViewModel.pinExists == true) { - router.go('/navigasi'); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('PIN yang anda masukkan salah')), - ); - } - }, - child: Text("Verifikasi PIN"), - ), - ], - ), - ), - ); - } -} diff --git a/lib/screen/launch/splash_screen.dart b/lib/screen/launch/splash_screen.dart deleted file mode 100644 index bfe9c2a..0000000 --- a/lib/screen/launch/splash_screen.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rijig_mobile/core/guide.dart'; -import 'package:rijig_mobile/core/router.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class SplashScreen extends StatelessWidget { - const SplashScreen({super.key}); - - @override - Widget build(BuildContext context) { - _checkLoginStatus(context); - - return Scaffold( - backgroundColor: whiteColor, - body: Stack( - children: [ - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Image.asset('assets/image/Go_Ride.png', height: 200), - ), - Align( - alignment: Alignment.center, - child: Padding( - padding: const EdgeInsets.only(bottom: 250.0), - child: Text( - 'Rijig', - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.bold, - color: Colors.blue, - fontFamily: 'Roboto', - ), - ), - ), - ), - ], - ), - ); - } - - Future _checkLoginStatus(BuildContext context) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false; - - await Future.delayed(Duration(seconds: 3)); - - if (isLoggedIn) { - router.go('/navigasi'); - } else { - router.go('/onboarding'); - } - } -} diff --git a/lib/viewmodel/auth_vmod.dart b/lib/viewmodel/auth_vmod.dart deleted file mode 100644 index 0127cf3..0000000 --- a/lib/viewmodel/auth_vmod.dart +++ /dev/null @@ -1,117 +0,0 @@ -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 login(String phone) async { - try { - isLoading = true; - errorMessage = null; - notifyListeners(); - - final response = await _authModel.login(phone); - - 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'; - } - } catch (e) { - errorMessage = 'Error: $e'; - } finally { - isLoading = false; - notifyListeners(); - } - } - - Future verifyOtp(String phone, String otp) async { - try { - isLoading = true; - errorMessage = null; - notifyListeners(); - - String deviceId = await getDeviceId(); - - var response = await _authModel.verifyOtp(phone, otp, deviceId); - - if (response != null && response.status == 200) { - await _secureStorage.write( - key: 'token', - value: response.data?['token'], - ); - await _secureStorage.write( - key: 'user_id', - value: response.data?['user_id'], - ); - await _secureStorage.write( - key: 'user_role', - value: response.data?['user_role'], - ); - - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setBool('isLoggedIn', true); - - var pinStatusResponse = await _pinModel.checkPinStatus( - response.data?['user_id'], - ); - - if (pinStatusResponse?.status == 200) { - pinExists = true; - } else { - pinExists = false; - } - - authModel = response; - notifyListeners(); - } else { - errorMessage = response?.message ?? 'Failed to verify OTP'; - } - } catch (e) { - errorMessage = 'Error: $e'; - } finally { - isLoading = false; - notifyListeners(); - } - } - - Future getAuthToken() async { - return await _secureStorage.read(key: 'token'); - } - - Future getUserId() async { - return await _secureStorage.read(key: 'user_id'); - } - - Future getUserRole() async { - return await _secureStorage.read(key: 'user_role'); - } - - Future logout() async { - await _secureStorage.delete(key: 'token'); - await _secureStorage.delete(key: 'user_id'); - await _secureStorage.delete(key: 'user_role'); - - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.remove('isLoggedIn'); - notifyListeners(); - } - - Future isUserLoggedIn() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getBool('isLoggedIn') ?? false; - } -} diff --git a/lib/viewmodel/userpin_vmod.dart b/lib/viewmodel/userpin_vmod.dart deleted file mode 100644 index 3bf6d06..0000000 --- a/lib/viewmodel/userpin_vmod.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rijig_mobile/model/userpin_model.dart'; - -class PinViewModel extends ChangeNotifier { - final PinModel _pinModel = PinModel(); - bool? pinExists; - - Future checkPinStatus(String userId) async { - try { - var response = await _pinModel.checkPinStatus(userId); - - if (response?.status == 200) { - pinExists = true; - } else { - pinExists = false; - } - notifyListeners(); - } catch (e) { - debugPrint('Error checking pin status: $e'); - pinExists = false; - notifyListeners(); - } - } - - Future createPin(String pin) async { - try { - var response = await _pinModel.setPin(pin); - - if (response?.status == 201) { - pinExists = true; - } else { - pinExists = false; - } - notifyListeners(); - } catch (e) { - debugPrint('Error creating pin: $e'); - pinExists = false; - notifyListeners(); - } - } - - Future verifyPin(String pin) async { - try { - var response = await _pinModel.verifyPin(pin); - - if (response?.status == 200) { - pinExists = true; - } else { - pinExists = false; - } - notifyListeners(); - } catch (e) { - debugPrint('Error verifying pin: $e'); - pinExists = false; - notifyListeners(); - } - } -} diff --git a/lib/widget/appbar.dart b/lib/widget/appbar.dart index e64dc16..641b51b 100644 --- a/lib/widget/appbar.dart +++ b/lib/widget/appbar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:iconsax_flutter/iconsax_flutter.dart'; -import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/core/router.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { diff --git a/lib/widget/buttoncard.dart b/lib/widget/buttoncard.dart index 0883dbf..98cd751 100644 --- a/lib/widget/buttoncard.dart +++ b/lib/widget/buttoncard.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; class CardButtonOne extends StatelessWidget { final String textButton; diff --git a/lib/widget/formfiled.dart b/lib/widget/formfiled.dart index 8b605dc..3c8b899 100644 --- a/lib/widget/formfiled.dart +++ b/lib/widget/formfiled.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; class FormFieldOne extends StatefulWidget { const FormFieldOne({ diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a05e955..01958b7 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import connectivity_plus import device_info_plus import flutter_secure_storage_macos import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b4fd9d2..e1d730c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" + url: "https://pub.dev" + source: hosted + version: "2.0.1" crypto: dependency: transitive description: @@ -73,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" device_info_plus: dependency: "direct main" description: @@ -224,6 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: f126a3e286b7f5b578bf436d5592968706c4c1de28a228b870ce375d9f743103 + url: "https://pub.dev" + source: hosted + version: "8.0.3" go_router: dependency: "direct main" description: @@ -280,6 +312,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + jwt_decoder: + dependency: "direct main" + description: + name: jwt_decoder + sha256: "54774aebf83f2923b99e6416b4ea915d47af3bde56884eb622de85feabbc559f" + url: "https://pub.dev" + source: hosted + version: "2.0.1" leak_tracker: dependency: transitive description: @@ -352,6 +392,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f98a0e..795985b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: concentric_transition: ^1.0.3 + connectivity_plus: ^6.1.4 cupertino_icons: ^1.0.8 device_info_plus: ^11.4.0 flutter: @@ -18,11 +19,13 @@ dependencies: flutter_secure_storage: ^9.2.4 flutter_svg: ^2.1.0 gap: ^3.0.1 + get_it: ^8.0.3 go_router: ^15.1.1 google_fonts: ^6.0.0 http: ^1.3.0 iconsax_flutter: ^1.0.0 intl: ^0.20.2 + jwt_decoder: ^2.0.1 pin_code_fields: ^8.0.1 provider: ^6.1.4 shared_preferences: ^2.3.3 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 0c50753..af1f996 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,12 @@ #include "generated_plugin_registrant.h" +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 4fc759c..f9ef886 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus flutter_secure_storage_windows )