From aac94f590fbb33159ec830b17c5ca9534d516eaa Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 15 Mar 2021 17:11:12 +0300 Subject: [PATCH] api methods added, --- lib/api/api_client.dart | 150 +++++++++++++++++++++ lib/api/authentication_api_client.dart | 3 + lib/api/tangheem_user_api_client.dart | 24 ++++ lib/api/user_api_client.dart | 1 + lib/classes/{const.dart => colors.dart} | 2 +- lib/classes/consts.dart | 6 + lib/classes/utils.dart | 47 +++++++ lib/exceptions/api_exception.dart | 28 ++++ lib/main.dart | 4 +- lib/models/authentication_user_model.dart | 55 ++++++++ lib/models/aya_model.dart | 108 +++++++++++++++ lib/models/surah_model.dart | 76 +++++++++++ lib/models/user_registration_model.dart | 46 +++++++ lib/ui/common_appbar.dart | 22 +-- lib/ui/dialogs/loading_dialog.dart | 59 ++++++++ lib/ui/dialogs/opt_dialog.dart | 14 +- lib/ui/screens/forgot_password_screen.dart | 12 +- lib/ui/screens/home_screen.dart | 71 ++++++---- lib/ui/screens/login_screen.dart | 12 +- lib/ui/screens/registration_screen.dart | 14 +- lib/ui/screens/surah_screen.dart | 100 ++++++++++++-- lib/ui/screens/tangheem_screen.dart | 28 ++-- lib/widgets/aya_player_widget.dart | 14 +- lib/widgets/common_dropdown_button.dart | 70 ++++++---- lib/widgets/common_textfield_widget.dart | 14 +- lib/widgets/otp_widget.dart | 12 +- pubspec.yaml | 1 + 27 files changed, 852 insertions(+), 141 deletions(-) create mode 100644 lib/api/api_client.dart create mode 100644 lib/api/authentication_api_client.dart create mode 100644 lib/api/tangheem_user_api_client.dart create mode 100644 lib/api/user_api_client.dart rename lib/classes/{const.dart => colors.dart} (97%) create mode 100644 lib/classes/consts.dart create mode 100644 lib/exceptions/api_exception.dart create mode 100644 lib/models/authentication_user_model.dart create mode 100644 lib/models/aya_model.dart create mode 100644 lib/models/surah_model.dart create mode 100644 lib/models/user_registration_model.dart create mode 100644 lib/ui/dialogs/loading_dialog.dart diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart new file mode 100644 index 0000000..39cb500 --- /dev/null +++ b/lib/api/api_client.dart @@ -0,0 +1,150 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:http/http.dart'; +import 'package:http/io_client.dart'; +import 'package:tangheem/exceptions/api_exception.dart'; + +typedef FactoryConstructor = U Function(dynamic); + +class APIError { + int errorCode; + String errorMessage; + + APIError(this.errorCode, this.errorMessage); + + Map toJson() => {'errorCode': errorCode, 'errorMessage': errorMessage}; + + @override + String toString() { + return jsonEncode(this); + } +} + +APIException _throwAPIException(Response response) { + switch (response.statusCode) { + case 400: + APIError apiError; + if (response.body != null && response.body.isNotEmpty) { + var jsonError = jsonDecode(response.body); + apiError = APIError(jsonError['errorCode'], jsonError['errorMessage']); + } + return APIException(APIException.BAD_REQUEST, error: apiError); + case 401: + return APIException(APIException.UNAUTHORIZED); + case 403: + return APIException(APIException.FORBIDDEN); + case 404: + return APIException(APIException.NOT_FOUND); + case 500: + return APIException(APIException.INTERNAL_SERVER_ERROR); + case 444: + var downloadUrl = response.headers["location"]; + return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl); + default: + return APIException(APIException.OTHER); + } +} + +class ApiClient { + static final ApiClient _instance = ApiClient._internal(); + ApiClient._internal(); + factory ApiClient() => _instance; + + Future postJsonForObject(FactoryConstructor factoryConstructor, String url, T jsonObject, + {String token, Map queryParameters, Map headers, int retryTimes = 0}) async { + var _headers = {'Accept': 'application/json'}; + if (headers != null && headers.isNotEmpty) { + _headers.addAll(headers); + } + print("Url:$url"); + var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes); + try { + var jsonData = jsonDecode(response.body); + return factoryConstructor(jsonData); + } catch (ex, tr) { + print('${ex.runtimeType}\n$ex\n$tr'); + throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex); + } + } + + Future postJsonForResponse(String url, T jsonObject, {String token, Map queryParameters, Map headers, int retryTimes = 0}) async { + String requestBody; + if (jsonObject != null) { + requestBody = jsonEncode(jsonObject); + if (headers == null) { + headers = {'Content-Type': 'application/json'}; + } else { + headers['Content-Type'] = 'application/json'; + } + } + + return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); + } + + Future _postForResponse(String url, requestBody, {String token, Map queryParameters, Map headers, int retryTimes = 0}) async { + try { + var _headers = {}; + if (token != null) { + _headers['Authorization'] = 'Bearer $token'; + } + + if (headers != null && headers.isNotEmpty) { + _headers.addAll(headers); + } + + if (queryParameters != null) { + var queryString = new Uri(queryParameters: queryParameters).query; + url = url + '?' + queryString; + } + var response = await _post(Uri.parse(url), body: requestBody, headers: _headers).timeout(Duration(seconds: 12)); + + if (response.statusCode >= 200 && response.statusCode < 300) { + return response; + } else { + throw _throwAPIException(response); + } + } on SocketException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } on HttpException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } on TimeoutException catch (e) { + throw APIException(APIException.TIMEOUT, arguments: e); + } on ClientException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } + } + + bool _certificateCheck(X509Certificate cert, String host, int port) => true; + + Future _withClient(Future Function(Client) fn) async { + var httpClient = HttpClient()..badCertificateCallback = _certificateCheck; + var client = IOClient(httpClient); + try { + return await fn(client); + } finally { + client.close(); + } + } + + Future _post(url, {Map headers, body, Encoding encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding)); +} diff --git a/lib/api/authentication_api_client.dart b/lib/api/authentication_api_client.dart new file mode 100644 index 0000000..66e8787 --- /dev/null +++ b/lib/api/authentication_api_client.dart @@ -0,0 +1,3 @@ +class AuthenticationApiClient{ + +} \ No newline at end of file diff --git a/lib/api/tangheem_user_api_client.dart b/lib/api/tangheem_user_api_client.dart new file mode 100644 index 0000000..f5a6d6e --- /dev/null +++ b/lib/api/tangheem_user_api_client.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +import 'package:tangheem/api/api_client.dart'; +import 'package:tangheem/classes/consts.dart'; +import 'package:tangheem/models/aya_model.dart'; +import 'package:tangheem/models/surah_model.dart'; + +class TangheemUserApiClient { + static final TangheemUserApiClient _instance = TangheemUserApiClient._internal(); + TangheemUserApiClient._internal(); + factory TangheemUserApiClient() => _instance; + + Future getSurahs() async { + String url = "${ApiConsts.tangheemUsers}AlSuar_Get"; + var postParams = {}; + return await ApiClient().postJsonForObject((json) => SurahModel.fromJson(json), url, postParams); + } + + Future getAyaByRange(int itemsPerPage, int currentPageNo, int surahID, int ayahFrom, int ayahTo) async { + String url = "${ApiConsts.tangheemUsers}AyatByRange_Get"; + var postParams = {"itemsPerPage": itemsPerPage, "currentPageNo": currentPageNo, "sortFieldName": "string", "isSortAsc": true, "surahID": surahID, "ayahFrom": ayahFrom, "ayahTo": ayahTo}; + return await ApiClient().postJsonForObject((json) => AyaModel.fromJson(json), url, postParams); + } +} diff --git a/lib/api/user_api_client.dart b/lib/api/user_api_client.dart new file mode 100644 index 0000000..da6f121 --- /dev/null +++ b/lib/api/user_api_client.dart @@ -0,0 +1 @@ +class UserApiClient {} diff --git a/lib/classes/const.dart b/lib/classes/colors.dart similarity index 97% rename from lib/classes/const.dart rename to lib/classes/colors.dart index b1cc55b..c24ae40 100644 --- a/lib/classes/const.dart +++ b/lib/classes/colors.dart @@ -1,6 +1,6 @@ import 'package:flutter/cupertino.dart'; -class Const { +class ColorConsts { static Color primaryBlack = Color(0xff1C2238); static Color primaryBlue = Color(0xff374061); diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart new file mode 100644 index 0000000..224df3b --- /dev/null +++ b/lib/classes/consts.dart @@ -0,0 +1,6 @@ +class ApiConsts { + static String baseUrl = "http://10.200.204.20:2801/"; // Local server + static String authentication = baseUrl + "api/Authentication/"; + static String tangheemUsers = baseUrl + "api/TangheemUsers/"; + static String user = baseUrl + "api/User/"; +} diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 8d367a1..26a0d33 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -1,9 +1,56 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:tangheem/exceptions/api_exception.dart'; +import 'package:tangheem/ui/dialogs/loading_dialog.dart'; +import 'colors.dart'; class Utils { + static bool _isLoadingVisible = false; static void showToast(String message) { Fluttertoast.showToast( msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 1, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); } + + static void showLoading(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _isLoadingVisible = true; + showDialog( + context: context, + barrierColor: ColorConsts.primaryBlack.withOpacity(0.5), + builder: (BuildContext context) => LoadingDialog(), + ).then((value) { + _isLoadingVisible = false; + }); + }); + } + + static void hideLoading(BuildContext context) { + if (_isLoadingVisible) { + _isLoadingVisible = false; + Navigator.of(context).pop(); + } + _isLoadingVisible = false; + } + + static void handleException(dynamic exception, Function(String) onErrorMessage) { + String errorMessage; + if (exception is APIException) { + if (exception.message == APIException.UNAUTHORIZED) { + return; + } else { + var message = exception.error?.errorMessage; + if (message == null) { + message = exception.message; + } + errorMessage = message; + } + } else { + errorMessage = APIException.UNKNOWN; + } + if (onErrorMessage != null) { + onErrorMessage(errorMessage); + } else { + showToast(errorMessage); + } + } } diff --git a/lib/exceptions/api_exception.dart b/lib/exceptions/api_exception.dart new file mode 100644 index 0000000..8c05a83 --- /dev/null +++ b/lib/exceptions/api_exception.dart @@ -0,0 +1,28 @@ +import 'dart:convert'; +import 'package:tangheem/api/api_client.dart'; + +class APIException implements Exception { + static const String BAD_REQUEST = 'api_common_bad_request'; + static const String UNAUTHORIZED = 'api_common_unauthorized'; + static const String FORBIDDEN = 'api_common_forbidden'; + static const String NOT_FOUND = 'api_common_not_found'; + static const String INTERNAL_SERVER_ERROR = 'api_common_internal_server_error'; + static const String UPGRADE_REQUIRED = 'api_common_upgrade_required'; + static const String BAD_RESPONSE_FORMAT = 'api_common_bad_response_format'; + static const String OTHER = 'api_common_http_error'; + static const String TIMEOUT = 'api_common_http_timeout'; + static const String UNKNOWN = 'unexpected_error'; + + final String message; + final APIError error; + final arguments; + + const APIException(this.message, {this.arguments, this.error}); + + Map toJson() => {'message': message, 'error': error, 'arguments': '$arguments'}; + + @override + String toString() { + return jsonEncode(this); + } +} diff --git a/lib/main.dart b/lib/main.dart index 2eee6e2..ac8557a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,7 @@ import 'package:tangheem/ui/screens/registration_screen.dart'; import 'package:tangheem/ui/screens/surah_screen.dart'; import 'package:tangheem/ui/screens/tangheem_screen.dart'; -import 'classes/const.dart'; +import 'classes/colors.dart'; void main() { runApp(Application()); @@ -63,7 +63,7 @@ class Application extends StatelessWidget { return ThemeData( fontFamily: 'DroidKufi', brightness: Brightness.light, - backgroundColor: Const.secondaryWhite, + backgroundColor: ColorConsts.secondaryWhite, primarySwatch: Colors.blue, primaryColor: Colors.white, primaryColorBrightness: Brightness.light, diff --git a/lib/models/authentication_user_model.dart b/lib/models/authentication_user_model.dart new file mode 100644 index 0000000..2be1ec1 --- /dev/null +++ b/lib/models/authentication_user_model.dart @@ -0,0 +1,55 @@ +class AuthenticationUserModel { + int totalItemsCount; + int statusCode; + String message; + Data data; + + AuthenticationUserModel( + {this.totalItemsCount, this.statusCode, this.message, this.data}); + + AuthenticationUserModel.fromJson(Map json) { + totalItemsCount = json['totalItemsCount']; + statusCode = json['statusCode']; + message = json['message']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + } + + Map toJson() { + final Map data = new Map(); + data['totalItemsCount'] = this.totalItemsCount; + data['statusCode'] = this.statusCode; + data['message'] = this.message; + if (this.data != null) { + data['data'] = this.data.toJson(); + } + return data; + } +} + +class Data { + String token; + String userId; + String email; + String mobileNumber; + String userName; + + Data({this.token, this.userId, this.email, this.mobileNumber, this.userName}); + + Data.fromJson(Map json) { + token = json['token']; + userId = json['userId']; + email = json['email']; + mobileNumber = json['mobileNumber']; + userName = json['userName']; + } + + Map toJson() { + final Map data = new Map(); + data['token'] = this.token; + data['userId'] = this.userId; + data['email'] = this.email; + data['mobileNumber'] = this.mobileNumber; + data['userName'] = this.userName; + return data; + } +} \ No newline at end of file diff --git a/lib/models/aya_model.dart b/lib/models/aya_model.dart new file mode 100644 index 0000000..035b970 --- /dev/null +++ b/lib/models/aya_model.dart @@ -0,0 +1,108 @@ +class AyaModel { + int totalItemsCount; + int statusCode; + String message; + List data; + + AyaModel({this.totalItemsCount, this.statusCode, this.message, this.data}); + + AyaModel.fromJson(Map json) { + totalItemsCount = json['totalItemsCount']; + statusCode = json['statusCode']; + message = json['message']; + if (json['data'] != null) { + data = new List(); + json['data'].forEach((v) { + data.add(new Data.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['totalItemsCount'] = this.totalItemsCount; + data['statusCode'] = this.statusCode; + data['message'] = this.message; + if (this.data != null) { + data['data'] = this.data.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Data { + int surahID; + String surahNameAR; + String surahNameEN; + int numberOfAyahs; + String englishNameTranslation; + int revelationID; + String revelationType; + int ayahID; + int numberInSurah; + int page; + int quarterID; + int juzID; + int manzil; + bool sajda; + String ayahText; + Null eighthsID; + + Data( + {this.surahID, + this.surahNameAR, + this.surahNameEN, + this.numberOfAyahs, + this.englishNameTranslation, + this.revelationID, + this.revelationType, + this.ayahID, + this.numberInSurah, + this.page, + this.quarterID, + this.juzID, + this.manzil, + this.sajda, + this.ayahText, + this.eighthsID}); + + Data.fromJson(Map json) { + surahID = json['surahID']; + surahNameAR = json['surahNameAR']; + surahNameEN = json['surahNameEN']; + numberOfAyahs = json['numberOfAyahs']; + englishNameTranslation = json['englishNameTranslation']; + revelationID = json['revelation_ID']; + revelationType = json['revelationType']; + ayahID = json['ayahID']; + numberInSurah = json['numberInSurah']; + page = json['page']; + quarterID = json['quarter_ID']; + juzID = json['juz_ID']; + manzil = json['manzil']; + sajda = json['sajda']; + ayahText = json['ayah_Text']; + eighthsID = json['eighths_ID']; + } + + Map toJson() { + final Map data = new Map(); + data['surahID'] = this.surahID; + data['surahNameAR'] = this.surahNameAR; + data['surahNameEN'] = this.surahNameEN; + data['numberOfAyahs'] = this.numberOfAyahs; + data['englishNameTranslation'] = this.englishNameTranslation; + data['revelation_ID'] = this.revelationID; + data['revelationType'] = this.revelationType; + data['ayahID'] = this.ayahID; + data['numberInSurah'] = this.numberInSurah; + data['page'] = this.page; + data['quarter_ID'] = this.quarterID; + data['juz_ID'] = this.juzID; + data['manzil'] = this.manzil; + data['sajda'] = this.sajda; + data['ayah_Text'] = this.ayahText; + data['eighths_ID'] = this.eighthsID; + return data; + } +} diff --git a/lib/models/surah_model.dart b/lib/models/surah_model.dart new file mode 100644 index 0000000..108a2ea --- /dev/null +++ b/lib/models/surah_model.dart @@ -0,0 +1,76 @@ +class SurahModel { + int totalItemsCount; + int statusCode; + String message; + List data; + + SurahModel({this.totalItemsCount, this.statusCode, this.message, this.data}); + + SurahModel.fromJson(Map json) { + totalItemsCount = json['totalItemsCount']; + statusCode = json['statusCode']; + message = json['message']; + if (json['data'] != null) { + data = new List(); + json['data'].forEach((v) { + data.add(new Data.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['totalItemsCount'] = this.totalItemsCount; + data['statusCode'] = this.statusCode; + data['message'] = this.message; + if (this.data != null) { + data['data'] = this.data.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Data { + int id; + int surahID; + String nameAR; + String nameEN; + int numberOfAyahs; + String englishNameTranslation; + int revelationID; + String revelationType; + + Data( + {this.id, + this.surahID, + this.nameAR, + this.nameEN, + this.numberOfAyahs, + this.englishNameTranslation, + this.revelationID, + this.revelationType}); + + Data.fromJson(Map json) { + id = json['id']; + surahID = json['surahID']; + nameAR = json['nameAR']; + nameEN = json['nameEN']; + numberOfAyahs = json['numberOfAyahs']; + englishNameTranslation = json['englishNameTranslation']; + revelationID = json['revelation_ID']; + revelationType = json['revelationType']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['surahID'] = this.surahID; + data['nameAR'] = this.nameAR; + data['nameEN'] = this.nameEN; + data['numberOfAyahs'] = this.numberOfAyahs; + data['englishNameTranslation'] = this.englishNameTranslation; + data['revelation_ID'] = this.revelationID; + data['revelationType'] = this.revelationType; + return data; + } +} diff --git a/lib/models/user_registration_model.dart b/lib/models/user_registration_model.dart new file mode 100644 index 0000000..71c1c72 --- /dev/null +++ b/lib/models/user_registration_model.dart @@ -0,0 +1,46 @@ +class UserRegistrationModel { + String userName; + String password; + String email; + String countryCode; + String mobileNumber; + bool isUserLock; + int gender; + int passWrongAttempt; + int statusId; + bool isEmailVerified; + bool isMobileVerified; + + UserRegistrationModel( + {this.userName, this.password, this.email, this.countryCode, this.mobileNumber, this.isUserLock, this.gender, this.passWrongAttempt, this.statusId, this.isEmailVerified, this.isMobileVerified}); + + UserRegistrationModel.fromJson(Map json) { + userName = json['userName']; + password = json['password']; + email = json['email']; + countryCode = json['countryCode']; + mobileNumber = json['mobileNumber']; + isUserLock = json['isUserLock']; + gender = json['gender']; + passWrongAttempt = json['passWrongAttempt']; + statusId = json['statusId']; + isEmailVerified = json['isEmailVerified']; + isMobileVerified = json['isMobileVerified']; + } + + Map toJson() { + final Map data = new Map(); + data['userName'] = this.userName; + data['password'] = this.password; + data['email'] = this.email; + data['countryCode'] = this.countryCode; + data['mobileNumber'] = this.mobileNumber; + data['isUserLock'] = this.isUserLock; + data['gender'] = this.gender; + data['passWrongAttempt'] = this.passWrongAttempt; + data['statusId'] = this.statusId; + data['isEmailVerified'] = this.isEmailVerified; + data['isMobileVerified'] = this.isMobileVerified; + return data; + } +} diff --git a/lib/ui/common_appbar.dart b/lib/ui/common_appbar.dart index 620d0c9..79586a5 100644 --- a/lib/ui/common_appbar.dart +++ b/lib/ui/common_appbar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/ui/screens/login_screen.dart'; class CommonAppbar extends StatefulWidget { @@ -48,7 +48,7 @@ class _CommonAppbarState extends State { children: [ if (!widget.isFirst) IconButton( - icon: Icon(widget.showDrawer ? Icons.menu : Icons.arrow_back_ios, color: Const.textGrey), + icon: Icon(widget.showDrawer ? Icons.menu : Icons.arrow_back_ios, color: ColorConsts.textGrey), padding: EdgeInsets.only(left: 16), onPressed: () { if (widget.showDrawer) { @@ -93,7 +93,7 @@ class _CommonAppbarState extends State { padding: EdgeInsets.only(left: 0, top: height), alignment: Alignment.centerLeft, child: IconButton( - icon: Icon(Icons.clear, color: Const.textGrey), + icon: Icon(Icons.clear, color: ColorConsts.textGrey), onPressed: () { if (_scaffoldKey.currentState.isDrawerOpen) { Navigator.pop(context); @@ -138,7 +138,7 @@ class _CommonAppbarState extends State { height: 40, margin: EdgeInsets.only(right: 17, left: 10), // color: Const.primaryBlack, - child: VerticalDivider(color: Const.primaryBlack, thickness: .7, width: 1), + child: VerticalDivider(color: ColorConsts.primaryBlack, thickness: .7, width: 1), ), ], ), @@ -155,7 +155,7 @@ class _CommonAppbarState extends State { height: 40, margin: EdgeInsets.only(right: 17, left: 10), // color: Const.primaryBlack, - child: VerticalDivider(color: Const.primaryBlack, thickness: .7, width: 1), + child: VerticalDivider(color: ColorConsts.primaryBlack, thickness: .7, width: 1), ), ], ), @@ -172,7 +172,7 @@ class _CommonAppbarState extends State { height: 40, margin: EdgeInsets.only(right: 17, left: 10), // color: Const.primaryBlack, - child: VerticalDivider(color: Const.primaryBlack, thickness: .7, width: 1), + child: VerticalDivider(color: ColorConsts.primaryBlack, thickness: .7, width: 1), ), ], ), @@ -189,7 +189,7 @@ class _CommonAppbarState extends State { height: 40, margin: EdgeInsets.only(right: 17, left: 10), // color: Const.primaryBlack, - child: VerticalDivider(color: Const.primaryBlack, thickness: .7, width: 1), + child: VerticalDivider(color: ColorConsts.primaryBlack, thickness: .7, width: 1), ), ], ), @@ -206,7 +206,7 @@ class _CommonAppbarState extends State { height: 40, margin: EdgeInsets.only(right: 17, left: 10), // color: Const.primaryBlack, - child: VerticalDivider(color: Const.primaryBlack, thickness: .7, width: 1), + child: VerticalDivider(color: ColorConsts.primaryBlack, thickness: .7, width: 1), ), ], ), @@ -283,7 +283,7 @@ class _CommonAppbarState extends State { stops: [0.0, 0.5], begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [Const.gradientPink, Const.gradientOrange], + colors: [ColorConsts.gradientPink, ColorConsts.gradientOrange], ) : null, ), @@ -292,10 +292,10 @@ class _CommonAppbarState extends State { children: [ Text( title, - style: TextStyle(fontSize: 14, color: isSelected ? Colors.white : Const.textGrey), + style: TextStyle(fontSize: 14, color: isSelected ? Colors.white : ColorConsts.textGrey), ), SizedBox(width: 8), - SvgPicture.asset(icon, height: 20, width: 20, color: isSelected ? Colors.white : Const.textGrey), + SvgPicture.asset(icon, height: 20, width: 20, color: isSelected ? Colors.white : ColorConsts.textGrey), ], ), ), diff --git a/lib/ui/dialogs/loading_dialog.dart b/lib/ui/dialogs/loading_dialog.dart new file mode 100644 index 0000000..e52cedb --- /dev/null +++ b/lib/ui/dialogs/loading_dialog.dart @@ -0,0 +1,59 @@ +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:tangheem/classes/colors.dart'; + +class LoadingDialog extends StatefulWidget { + LoadingDialog({Key key}) : super(key: key); + + @override + _LoadingDialogState createState() { + return _LoadingDialogState(); + } +} + +class _LoadingDialogState extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + insetPadding: EdgeInsets.symmetric(horizontal: 60.0, vertical: 24.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + elevation: 0, + backgroundColor: Colors.transparent, + child: Directionality( + textDirection: TextDirection.rtl, + child: Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + ), + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), + child: SizedBox( + height: 32, + width: 32, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(ColorConsts.textGrey), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/dialogs/opt_dialog.dart b/lib/ui/dialogs/opt_dialog.dart index 6a41217..8305a4b 100644 --- a/lib/ui/dialogs/opt_dialog.dart +++ b/lib/ui/dialogs/opt_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/widgets/otp_widget.dart'; class OTPDialog extends StatefulWidget { @@ -42,7 +42,7 @@ class _OTPDialogState extends State { child: Container( width: double.infinity, decoration: BoxDecoration( - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, borderRadius: BorderRadius.circular(16), ), padding: EdgeInsets.symmetric(vertical: 32, horizontal: 16), @@ -63,7 +63,7 @@ class _OTPDialogState extends State { border: Border.all(width: 1, color: Colors.white), ), child: OTPWidget( - autofocus: true, + autoFocus: true, controller: _pinPutController, defaultBorderColor: Colors.transparent, maxLength: 4, @@ -73,12 +73,12 @@ class _OTPDialogState extends State { hasError = false; }); }, - pinBoxColor: Const.secondaryWhite.withOpacity(0.2), + pinBoxColor: ColorConsts.secondaryWhite.withOpacity(0.2), onDone: (text) {}, textBorderColor: Colors.transparent, pinBoxWidth: 40, pinBoxHeight: 40, - pinTextStyle: TextStyle(fontSize: 20.0, color: Colors.white), + pinTextStyle: TextStyle(fontSize: 20.0, color: Colors.white,height: .2), pinTextAnimatedSwitcherTransition: ProvidedPinBoxTextAnimation.scalingTransition, pinTextAnimatedSwitcherDuration: Duration(milliseconds: 300), keyboardType: TextInputType.number, @@ -94,7 +94,7 @@ class _OTPDialogState extends State { style: TextButton.styleFrom( primary: Colors.white, padding: EdgeInsets.all(2), - backgroundColor: Const.secondaryPink, + backgroundColor: ColorConsts.secondaryPink, textStyle: TextStyle(fontSize: 14, fontFamily: "DroidKufi"), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), @@ -111,7 +111,7 @@ class _OTPDialogState extends State { BoxDecoration get _pinPutDecoration { return BoxDecoration( - color: Const.secondaryWhite.withOpacity(0.2), + color: ColorConsts.secondaryWhite.withOpacity(0.2), //border: Border.all(color: Colors.deepPurpleAccent), //borderRadius: BorderRadius.circular(0.0), ); diff --git a/lib/ui/screens/forgot_password_screen.dart b/lib/ui/screens/forgot_password_screen.dart index aef1032..582f7c9 100644 --- a/lib/ui/screens/forgot_password_screen.dart +++ b/lib/ui/screens/forgot_password_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/ui/dialogs/opt_dialog.dart'; import 'package:tangheem/widgets/common_textfield_widget.dart'; @@ -33,7 +33,7 @@ class _ForgotPasswordScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Const.secondaryWhite, + backgroundColor: ColorConsts.secondaryWhite, body: SingleChildScrollView( padding: EdgeInsets.all(32.0), physics: BouncingScrollPhysics(), @@ -50,14 +50,14 @@ class _ForgotPasswordScreenState extends State { ), Text( "نسيت كلمة المرور؟", - style: TextStyle(fontSize: 22, color: Const.primaryBlue), + style: TextStyle(fontSize: 22, color: ColorConsts.primaryBlue), ), Container( margin: EdgeInsets.only(top: 16), width: double.infinity, padding: EdgeInsets.all(32.0), decoration: BoxDecoration( - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8), @@ -92,7 +92,7 @@ class _ForgotPasswordScreenState extends State { }, style: TextButton.styleFrom( primary: Colors.white, - backgroundColor: Const.secondaryPink, + backgroundColor: ColorConsts.secondaryPink, textStyle: TextStyle(fontSize: 16, fontFamily: "DroidKufi"), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), @@ -114,7 +114,7 @@ class _ForgotPasswordScreenState extends State { getOTP() { showDialog( context: context, - barrierColor: Const.secondaryWhite.withOpacity(0.8), + barrierColor: ColorConsts.secondaryWhite.withOpacity(0.8), builder: (BuildContext context) => OTPDialog(), ); } diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart index cabbc77..fcd473c 100644 --- a/lib/ui/screens/home_screen.dart +++ b/lib/ui/screens/home_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/ui/screens/surah_screen.dart'; import 'package:tangheem/ui/screens/tangheem_screen.dart'; import 'package:tangheem/widgets/common_dropdown_button.dart'; @@ -47,44 +47,63 @@ class _HomeScreenState extends State { children: [ Text( "موسوعةالأداء القرآني", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Const.primaryBlue, height: 1), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1), ), SizedBox(height: 4), Text( "للأساليب اللغوية", - style: TextStyle(fontSize: 20, color: Const.primaryBlue, height: 1), + style: TextStyle(fontSize: 20, color: ColorConsts.primaryBlue, height: 1), ), SizedBox(height: 8), Text( "تساعدك موسوعة \"تنغيم\" على أداء الأساليب اللغوية القرآنية (كالاستفهام والأمر والنهي والإتمام) بما يخدم معنى الآية. وقد أشرف عليها متخصصون في اللغة العربية والدراسات القرآنية.", - style: TextStyle(fontSize: 14, color: Const.textGrey, height: 1), + style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1), ), SizedBox(height: 32), - Row( - children: [ - Expanded( - child: CommonDropDownButton("الأسلوب اللغوي", onPressed: () {}), - ), - SizedBox(width: 8), - Expanded( - child: CommonDropDownButton("السورة", onPressed: () {}), - ) - ], - ), + // Row( + // children: [ + // Expanded( + // child: CommonDropDownButton("الأسلوب اللغوي", onPressed: () {}), + // ), + // SizedBox(width: 8), + // Expanded( + // child: CommonDropDownButton("السورة", onPressed: () {}), + // ) + // ], + // ), SizedBox(height: 16), - CommonDropDownButton( - "ابدأ البحث", - color: Const.secondaryPink, - icon: "assets/icons/go_forward.svg", - widthHeight: 20, - isExpanded: false, - iconColor: Colors.white, - onPressed: () async { + InkWell( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { _searchFocusNode.unfocus(); _searchFocusNode.canRequestFocus = false; await Navigator.pushNamed(context, TangheemScreen.routeName); _searchFocusNode.canRequestFocus = true; }, + child: Container( + height: 40, + decoration: BoxDecoration( + color: ColorConsts.secondaryPink, + borderRadius: BorderRadius.circular(6), + ), + padding: EdgeInsets.fromLTRB(8, 8, 8, 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "ابدأ البحث", + maxLines: 1, + // overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: Colors.white), + ), + SizedBox(width: 8), + SvgPicture.asset("assets/icons/go_forward.svg", width: 20, height: 20, color: Colors.white), + ], + ), + ), ), SizedBox(height: 16), Container( @@ -93,13 +112,13 @@ class _HomeScreenState extends State { child: TextField( controller: _searchController, focusNode: _searchFocusNode, - style: TextStyle(color: Const.primaryBlack, fontSize: 14), + style: TextStyle(color: ColorConsts.primaryBlack, fontSize: 14), decoration: InputDecoration( contentPadding: EdgeInsets.fromLTRB(4, 4, 4, 4), alignLabelWithHint: true, fillColor: Colors.white, filled: true, - hintStyle: TextStyle(color: Const.textHintGrey, fontSize: 12), + hintStyle: TextStyle(color: ColorConsts.textHintGrey, fontSize: 12), hintText: "البحث عن سورة أو آية", prefixIconConstraints: BoxConstraints(maxHeight: 16), prefixIcon: Padding( @@ -126,7 +145,7 @@ class _HomeScreenState extends State { style: TextStyle(fontSize: 14, color: Colors.white), ), decoration: BoxDecoration( - color: Const.secondaryPink, + color: ColorConsts.secondaryPink, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(6), topLeft: Radius.circular(6), diff --git a/lib/ui/screens/login_screen.dart b/lib/ui/screens/login_screen.dart index db60701..ebed07f 100644 --- a/lib/ui/screens/login_screen.dart +++ b/lib/ui/screens/login_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/ui/screens/forgot_password_screen.dart'; import 'package:tangheem/ui/screens/registration_screen.dart'; @@ -35,7 +35,7 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Const.secondaryWhite, + backgroundColor: ColorConsts.secondaryWhite, body: SafeArea( child: Directionality( textDirection: TextDirection.rtl, @@ -55,13 +55,13 @@ class _LoginScreenState extends State { ), Text( "تسجيل الدخول", - style: TextStyle(fontSize: 22, color: Const.primaryBlue), + style: TextStyle(fontSize: 22, color: ColorConsts.primaryBlue), ), Container( margin: EdgeInsets.only(top: 16), width: double.infinity, padding: EdgeInsets.all(32.0), - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -86,7 +86,7 @@ class _LoginScreenState extends State { }, style: TextButton.styleFrom( primary: Colors.white, - backgroundColor: Const.secondaryPink, + backgroundColor: ColorConsts.secondaryPink, textStyle: TextStyle(fontSize: 16, fontFamily: "DroidKufi"), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), @@ -139,7 +139,7 @@ class _LoginScreenState extends State { height: 50, alignment: Alignment.center, decoration: BoxDecoration( - color: Const.tertiaryPurple, + color: ColorConsts.tertiaryPurple, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8), diff --git a/lib/ui/screens/registration_screen.dart b/lib/ui/screens/registration_screen.dart index 144b31c..9e31dbc 100644 --- a/lib/ui/screens/registration_screen.dart +++ b/lib/ui/screens/registration_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/widgets/common_textfield_widget.dart'; import 'package:tangheem/extensions/email_validator.dart'; @@ -39,7 +39,7 @@ class _RegistrationScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Const.secondaryWhite, + backgroundColor: ColorConsts.secondaryWhite, body: SingleChildScrollView( padding: EdgeInsets.all(32.0), physics: BouncingScrollPhysics(), @@ -56,14 +56,14 @@ class _RegistrationScreenState extends State { ), Text( "انشاء حساب جديد", - style: TextStyle(fontSize: 22, color: Const.primaryBlue), + style: TextStyle(fontSize: 22, color: ColorConsts.primaryBlue), ), Padding( padding: EdgeInsets.all(8), child: Text( "قم بتعبئة بيانات طلب انشاء الحساب و ستصلك رسالة تأكيد, ومن ثم يمكنك الدخول لحسابك الجديد", textAlign: TextAlign.center, - style: TextStyle(fontSize: 14, color: Const.primaryBlue, height: 1), + style: TextStyle(fontSize: 14, color: ColorConsts.primaryBlue, height: 1), ), ), Container( @@ -71,7 +71,7 @@ class _RegistrationScreenState extends State { width: double.infinity, padding: EdgeInsets.all(32.0), decoration: BoxDecoration( - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8), @@ -171,7 +171,7 @@ class _RegistrationScreenState extends State { }, style: TextButton.styleFrom( primary: Colors.white, - backgroundColor: Const.secondaryPink, + backgroundColor: ColorConsts.secondaryPink, textStyle: TextStyle(fontSize: 16, fontFamily: "DroidKufi"), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), @@ -216,7 +216,7 @@ class _RegistrationScreenState extends State { Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, borderRadius: BorderRadius.only( topLeft: Radius.circular(16), topRight: Radius.circular(16), diff --git a/lib/ui/screens/surah_screen.dart b/lib/ui/screens/surah_screen.dart index ec9bfee..af16d40 100644 --- a/lib/ui/screens/surah_screen.dart +++ b/lib/ui/screens/surah_screen.dart @@ -1,7 +1,11 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/api/tangheem_user_api_client.dart'; +import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/utils.dart'; +import 'package:tangheem/models/aya_model.dart'; +import 'package:tangheem/models/surah_model.dart'; import 'package:tangheem/widgets/aya_player_widget.dart'; import 'package:tangheem/widgets/common_dropdown_button.dart'; @@ -18,13 +22,65 @@ class SurahScreen extends StatefulWidget { } class _SurahScreenState extends State { + int _selectedSurah = 0; + int _selectedFromAya = 0; + int _selectedToAya = 0; + + int _currentPage = 0; + int _ayaInPage = 0; + List _surahList = []; + List _fromAyaList = []; + List _toAyaList = []; + + SurahModel _surahModel; + AyaModel _ayaModel; + @override void initState() { super.initState(); - searchQuery(); + getSurahAndAya(); + } + + void getSurahAndAya() async { + Utils.showLoading(context); + try { + _surahModel = await TangheemUserApiClient().getSurahs(); + _surahList = _surahModel.data.map((element) => element.nameEN).toList(); + filterData(); + } catch (ex, tr) { + Utils.handleException(ex, null); + } finally { + Utils.hideLoading(context); + } } - void searchQuery() {} + int numberOfAyah = 0; + void filterData() { + numberOfAyah = _surahModel?.data[_selectedSurah]?.numberOfAyahs ?? 0; + var filteredAyahList = List.generate(getNextMultiple(numberOfAyah), (index) => index + 1).toList().where((element) => element == 1 || (element % 5) == 0).toList() ?? []; + _fromAyaList = filteredAyahList.getRange(0, filteredAyahList.length - 1)?.toList() ?? []; + _toAyaList = filteredAyahList.getRange(1, filteredAyahList.length)?.toList() ?? []; + getAyaByRange(); + } + + void getAyaByRange() async { + Utils.showLoading(context); + try { + _ayaModel = await TangheemUserApiClient().getAyaByRange(5, 1, _selectedSurah + 1, _fromAyaList[_selectedFromAya], _toAyaList[_selectedToAya]); + setState(() {}); + } catch (ex, tr) { + Utils.handleException(ex, null); + } finally { + Utils.hideLoading(context); + } + } + + int getNextMultiple(int num) { + int multipleOf = 5; + int nextDiff = multipleOf - (num % multipleOf); + int total = num + nextDiff; + return total; + } @override void dispose() { @@ -41,21 +97,39 @@ class _SurahScreenState extends State { children: [ Text( "اقرأ القرآن الكريم", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Const.primaryBlue, height: 1), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1), ), SizedBox(height: 16), Row( children: [ Expanded( - child: CommonDropDownButton("سورة البقرة", onPressed: () {}), + child: CommonDropDownButton(_selectedSurah, list: _surahList, onSelect: (index) { + if (_selectedSurah != index) { + _selectedSurah = index; + _selectedToAya = 0; + _selectedFromAya = 0; + filterData(); + } + }), ), SizedBox(width: 4), Expanded( - child: CommonDropDownButton("من الاية" + " 158", onPressed: () {}), + child: CommonDropDownButton(_selectedFromAya, list: _fromAyaList.map((e) => "من الاية" + " $e").toList(), onSelect: (index) { + if (_selectedFromAya != index) { + _selectedFromAya = index; + _selectedToAya = index; + filterData(); + } + }), ), SizedBox(width: 4), Expanded( - child: CommonDropDownButton("الى الاية" + " 123", onPressed: () {}), + child: CommonDropDownButton(_selectedToAya, list: _toAyaList.map((e) => "الى الاية" + " $e").toList(), onSelect: (index) { + if (_selectedToAya != index) { + _selectedToAya = index; + filterData(); + } + }), ), ], ), @@ -85,17 +159,17 @@ class _SurahScreenState extends State { Text( "بسم الله الرحمن الرحيم", textAlign: TextAlign.center, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Const.primaryBlue, height: 1), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1), ), SizedBox(height: 8), Container( padding: EdgeInsets.only(left: 4, right: 4), child: Text( - "إِنَّ الصَّفَا وَالْمَرْوَةَ مِنْ شَعَائِرِ اللَّهِ ۖ فَمَنْ حَجَّ الْبَيْتَ أَوِ اعْتَمَرَ فَلَا جُنَاحَ عَلَيْهِ أَنْ يَطَّوَّفَ بِهِمَا ۚ وَمَنْ تَطَوَّعَ خَيْرًا فَإِنَّ اللَّهَ شَاكِرٌ عَلِيمٌ ﴿158﴾ إِنَّ الَّذِينَ يَكْتُمُونَ مَا أَنْزَلْنَا مِنَ الْبَيِّنَاتِ وَالْهُدَىٰ مِنْ بَعْدِ مَا بَيَّنَّاهُ لِلنَّاسِ فِي الْكِتَابِ ۙ أُولَٰئِكَ يَلْعَنُهُمُ اللَّهُ وَيَلْعَنُهُمُ اللَّاعِنُونَ ﴿159﴾ إِلَّا الَّذِينَ تَابُوا وَأَصْلَحُوا وَبَيَّنُوا فَأُولَٰئِكَ أَتُوبُ عَلَيْهِمْ ۚ وَأَنَا التَّوَّابُ الرَّحِيمُ ﴿160﴾ إِنَّ الَّذِينَ كَفَرُوا وَمَاتُوا وَهُمْ كُفَّارٌ أُولَٰئِكَ عَلَيْهِمْ لَعْنَةُ اللَّهِ وَالْمَلَائِكَةِ وَالنَّاسِ أَجْمَعِينَ ﴿161﴾ خَالِدِينَ فِيهَا ۖ لَا يُخَفَّفُ عَنْهُمُ الْعَذَابُ وَلَا هُمْ يُنْظَرُونَ ﴿162﴾ وَإِلَٰهُكُمْ إِلَٰهٌ وَاحِدٌ ۖ لَا إِلَٰهَ إِلَّا هُوَ الرَّحْمَٰنُ الرَّحِيمُ ﴿163﴾إِنَّ فِي خَلْقِ السَّمَاوَاتِ وَالْأَرْضِ وَاخْتِلَافِ اللَّيْلِ وَالنَّهَارِ وَالْفُلْكِ الَّتِي تَجْرِي فِي الْبَحْرِ بِمَا يَنْفَعُ النَّاسَ وَمَا أَنْزَلَ اللَّهُ مِنَ السَّمَاءِ مِنْ مَاءٍ فَأَحْيَا بِهِ الْأَرْضَ بَعْدَ مَوْتِهَا وَبَثَّ فِيهَا مِنْ كُلِّ دَابَّةٍ وَتَصْرِيفِ الرِّيَاحِ وَالسَّحَابِ الْمُسَخَّرِ بَيْنَ السَّمَاءِ وَالْأَرْضِ لَآيَاتٍ لِقَوْمٍ يَعْقِلُونَ ﴿164﴾ وَمِنَ النَّاسِ مَنْ يَتَّخِذُ مِنْ دُونِ اللَّهِ أَنْدَادًا يُحِبُّونَهُمْ كَحُبِّ اللَّهِ ۖ وَالَّذِينَ آمَنُوا أَشَدُّ حُبًّا لِلَّهِ ۗ وَلَوْ يَرَى الَّذِينَ ظَلَمُوا إِذْ يَرَوْنَ الْعَذَابَ أَنَّ الْقُوَّةَ لِلَّهِ جَمِيعًا وَأَنَّ اللَّهَ شَدِيدُ الْعَذَابِ ﴿165﴾ إِذْ تَبَرَّأَ الَّذِينَ اتُّبِعُوا مِنَ الَّذِينَ اتَّبَعُوا وَرَأَوُا الْعَذَابَ وَتَقَطَّعَتْ بِهِمُ الْأَسْبَابُ ﴿166﴾ وَقَالَ الَّذِينَ اتَّبَعُوا لَوْ أَنَّ لَنَا كَرَّةً فَنَتَبَرَّأَ مِنْهُمْ كَمَا تَبَرَّءُوا مِنَّا ۗ كَذَٰلِكَ يُرِيهِمُ اللَّهُ أَعْمَالَهُمْ حَسَرَاتٍ عَلَيْهِمْ ۖ وَمَا هُمْ بِخَارِجِينَ مِنَ النَّارِ ﴿167﴾4", + _ayaModel?.data?.map((e) => e.ayahText)?.toList()?.reduce((value, element) => value + element) ?? "", textAlign: TextAlign.center, style: TextStyle( fontFamily: "UthmanicHafs", - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, fontSize: 18, fontWeight: FontWeight.bold, ), @@ -107,7 +181,7 @@ class _SurahScreenState extends State { SizedBox(height: 8), Text( "1 2 3 4 5 6 7 8 9", - style: TextStyle(fontSize: 16, color: Const.textHintGrey), + style: TextStyle(fontSize: 16, color: ColorConsts.textHintGrey), ), SizedBox(height: 4), Row( @@ -138,7 +212,7 @@ class _SurahScreenState extends State { SizedBox(width: 4), Text( text, - style: TextStyle(color: Const.textGrey), + style: TextStyle(color: ColorConsts.textGrey), ), ], ), @@ -154,7 +228,7 @@ class _SurahScreenState extends State { children: [ Text( text, - style: TextStyle(color: Const.textGrey), + style: TextStyle(color: ColorConsts.textGrey), ), SizedBox(width: 4), SvgPicture.asset(icon, height: 12, width: 12), diff --git a/lib/ui/screens/tangheem_screen.dart b/lib/ui/screens/tangheem_screen.dart index bc85ae6..76952a5 100644 --- a/lib/ui/screens/tangheem_screen.dart +++ b/lib/ui/screens/tangheem_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/widgets/aya_player_widget.dart'; class TangheemScreen extends StatefulWidget { @@ -51,12 +51,12 @@ class _TangheemScreenState extends State { children: [ Text( "تنغيم أسلوب الإتمام", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Const.primaryBlue, height: 1.5), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1.5), ), SizedBox(height: 8), Text( "هنا نضع\" تعريف بالاستفهام وتداخل الأساليب\"", - style: TextStyle(fontSize: 14, color: Const.textGrey, height: 1), + style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1), ), SizedBox(height: 8), Expanded( @@ -79,7 +79,7 @@ class _TangheemScreenState extends State { textAlign: TextAlign.center, style: TextStyle( fontFamily: "UthmanicHafs", - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, fontSize: 19, fontWeight: FontWeight.bold, ), @@ -96,9 +96,9 @@ class _TangheemScreenState extends State { alignment: Alignment.centerRight, child: Text( "نوع جملة الاتمام", - style: TextStyle(fontWeight: FontWeight.bold, color: Const.secondaryOrange), + style: TextStyle(fontWeight: FontWeight.bold, color: ColorConsts.secondaryOrange), ), - color: Const.secondaryWhite, + color: ColorConsts.secondaryWhite, ), ), ), @@ -112,16 +112,16 @@ class _TangheemScreenState extends State { alignment: Alignment.centerRight, child: Text( "استثنائية", - style: TextStyle(color: Const.primaryBlack), + style: TextStyle(color: ColorConsts.primaryBlack), ), - color: Const.secondaryWhite, + color: ColorConsts.secondaryWhite, ), ), ) ], ), Container( - color: Const.primaryBlue, + color: ColorConsts.primaryBlue, margin: EdgeInsets.only(top: 8, bottom: 8), padding: EdgeInsets.all(8), child: Column( @@ -171,7 +171,7 @@ class _TangheemScreenState extends State { SizedBox(width: 4), Text( text, - style: TextStyle(color: Const.textGrey), + style: TextStyle(color: ColorConsts.textGrey), ), ], ), @@ -187,7 +187,7 @@ class _TangheemScreenState extends State { children: [ Text( text, - style: TextStyle(color: Const.textGrey), + style: TextStyle(color: ColorConsts.textGrey), ), SizedBox(width: 4), SvgPicture.asset(icon, height: 12, width: 12), @@ -213,7 +213,7 @@ class _TangheemScreenState extends State { }, itemBuilder: (context, index) { return Container( - color: Const.secondaryWhite, + color: ColorConsts.secondaryWhite, padding: EdgeInsets.all(8), child: Column( mainAxisSize: MainAxisSize.min, @@ -222,12 +222,12 @@ class _TangheemScreenState extends State { children: [ Text( temp[index].title, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12, color: Const.secondaryOrange), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12, color: ColorConsts.secondaryOrange), ), SizedBox(height: 4), Text( temp[index].description, - style: TextStyle(fontSize: 12, color: Const.secondaryPink), + style: TextStyle(fontSize: 12, color: ColorConsts.secondaryPink), ), ], ), diff --git a/lib/widgets/aya_player_widget.dart b/lib/widgets/aya_player_widget.dart index a08d5c6..ebb713f 100644 --- a/lib/widgets/aya_player_widget.dart +++ b/lib/widgets/aya_player_widget.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; class AyaPlayerWidget extends StatefulWidget { AyaPlayerWidget({Key key}) : super(key: key); @@ -71,12 +71,12 @@ class _AyaPlayerWidgetState extends State { "سورة البقسورة", maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Const.primaryBlack, height: 1), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlack, height: 1), ), SizedBox(height: 4), Text( "الشيخ عبد الشيخ عبد العزيز الزهراني", - style: TextStyle(fontSize: 10, color: Const.textGrey1, height: 1), + style: TextStyle(fontSize: 10, color: ColorConsts.textGrey1, height: 1), ), ], ), @@ -104,11 +104,11 @@ class _AyaPlayerWidgetState extends State { Expanded( child: SliderTheme( data: SliderTheme.of(context).copyWith( - activeTrackColor: Const.sliderBackground, - inactiveTrackColor: Const.secondaryOrange, + activeTrackColor: ColorConsts.sliderBackground, + inactiveTrackColor: ColorConsts.secondaryOrange, // trackShape: RoundedRectRangeSliderTrackShape(), trackHeight: 8.0, - thumbColor: Const.primaryBlack, + thumbColor: ColorConsts.primaryBlack, thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.0), overlayColor: Colors.red.withAlpha(32), overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0), @@ -125,7 +125,7 @@ class _AyaPlayerWidgetState extends State { ), Text( "06:00", - style: TextStyle(color: Const.textGrey1, height: 1.1, fontFamily: "Roboto"), + style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto"), ), ], ) diff --git a/lib/widgets/common_dropdown_button.dart b/lib/widgets/common_dropdown_button.dart index 9c1a713..52b8257 100644 --- a/lib/widgets/common_dropdown_button.dart +++ b/lib/widgets/common_dropdown_button.dart @@ -1,17 +1,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; class CommonDropDownButton extends StatelessWidget { + final int index; final String text; final String icon; final Color iconColor; final Color color; - final bool isExpanded; + final bool isDropDown; final VoidCallback onPressed; + final Function(int) onSelect; final double widthHeight; + final List list; - CommonDropDownButton(this.text, {Key key, this.onPressed, this.isExpanded = true, this.color, this.icon, this.widthHeight, this.iconColor}) : super(key: key); + CommonDropDownButton(this.index, {Key key, this.onPressed, this.isDropDown = true, this.text = "", this.color, this.icon, this.widthHeight, this.iconColor, this.onSelect, this.list}) + : super(key: key); @override Widget build(BuildContext context) { @@ -22,38 +26,48 @@ class CommonDropDownButton extends StatelessWidget { child: Container( height: 40, decoration: BoxDecoration( - color: color ?? Const.primaryBlue, + color: color ?? ColorConsts.primaryBlue, borderRadius: BorderRadius.circular(6), ), padding: EdgeInsets.fromLTRB(8, 8, 8, 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - isExpanded - ? Expanded( - child: Text( - text, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 12, color: Colors.white), - ), - ) - : Text( - text, - maxLines: 1, - // overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 12, color: Colors.white), - ), - SizedBox(width: 4), - SvgPicture.asset( + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: isDropDown, + dropdownColor: ColorConsts.secondaryWhite, + icon: SvgPicture.asset( icon ?? "assets/icons/drop_menu.svg", width: widthHeight ?? 10, height: widthHeight ?? 5, - color: iconColor ?? Const.secondaryOrange, + color: iconColor ?? ColorConsts.secondaryOrange, ), - ], + hint: Text( + list.isNotEmpty ? list[index] : text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: Colors.white), + ), + focusColor: Colors.white, + style: TextStyle(color: Colors.white), + items: !isDropDown + ? [] + : [ + for (int i = 0; i < list.length; i++) + DropdownMenuItem( + value: i, + child: SizedBox( + width: double.infinity, + child: Text( + list[i], + maxLines: 1, + textDirection: TextDirection.rtl, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 14, color: ColorConsts.primaryBlack), + ), + ), + ) + ], + onChanged: onSelect, + ), ), ), ); diff --git a/lib/widgets/common_textfield_widget.dart b/lib/widgets/common_textfield_widget.dart index 9a92e04..08d6c8e 100644 --- a/lib/widgets/common_textfield_widget.dart +++ b/lib/widgets/common_textfield_widget.dart @@ -1,6 +1,7 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tangheem/classes/const.dart'; +import 'package:tangheem/classes/colors.dart'; class CommonTextFieldWidget extends StatelessWidget { final String hint; @@ -21,21 +22,22 @@ class CommonTextFieldWidget extends StatelessWidget { controller: controller, obscureText: isPassword, textInputAction: TextInputAction.next, - style: TextStyle(color: Const.primaryBlack, fontSize: 14), - cursorColor: Const.primaryBlue, + style: TextStyle(color: ColorConsts.primaryBlack, fontSize: 14), + cursorColor: ColorConsts.primaryBlue, + readOnly: onTap != null, onTap: onTap, decoration: InputDecoration( contentPadding: EdgeInsets.fromLTRB(4, 4, 8, 4), alignLabelWithHint: true, fillColor: Colors.white, filled: true, - hintStyle: TextStyle(color: Const.textGrey2, fontSize: 14), + hintStyle: TextStyle(color: ColorConsts.textGrey2, fontSize: 14), hintText: hint, suffixIcon: !showSuffix ? null : Padding( padding: EdgeInsets.only(left: 8, right: 0), - child: Icon(Icons.keyboard_arrow_down, size: 18, color: Const.secondaryOrange), + child: Icon(Icons.keyboard_arrow_down, size: 18, color: ColorConsts.secondaryOrange), ), suffixIconConstraints: !showSuffix ? null : BoxConstraints(maxHeight: 30), prefixIconConstraints: prefixIcon == null ? prefixIcon : BoxConstraints(maxHeight: 18), @@ -43,7 +45,7 @@ class CommonTextFieldWidget extends StatelessWidget { ? prefixIcon : Padding( padding: EdgeInsets.only(left: 4, right: 0), - child: SvgPicture.asset(prefixIcon, color: Const.textGrey2), + child: SvgPicture.asset(prefixIcon, color: ColorConsts.textGrey2), ), border: OutlineInputBorder(borderRadius: BorderRadius.circular(6), borderSide: BorderSide.none), ), diff --git a/lib/widgets/otp_widget.dart b/lib/widgets/otp_widget.dart index d9fb2c8..d26ff8c 100644 --- a/lib/widgets/otp_widget.dart +++ b/lib/widgets/otp_widget.dart @@ -22,7 +22,6 @@ class ProvidedPinBoxTextAnimation { } class OTPWidget extends StatefulWidget { - final bool isCupertino; final int maxLength; final TextEditingController controller; @@ -40,7 +39,7 @@ class OTPWidget extends StatefulWidget { final Color errorBorderColor; final Color textBorderColor; final Function(String) onTextChanged; - final bool autofocus; + final bool autoFocus; final FocusNode focusNode; final AnimatedSwitcherTransitionBuilder pinTextAnimatedSwitcherTransition; final Duration pinTextAnimatedSwitcherDuration; @@ -50,7 +49,6 @@ class OTPWidget extends StatefulWidget { const OTPWidget({ Key key, - this.isCupertino: false, this.maxLength: 4, this.controller, this.pinBoxWidth: 70.0, @@ -64,7 +62,7 @@ class OTPWidget extends StatefulWidget { this.hasError: false, this.errorBorderColor: Colors.red, this.onTextChanged, - this.autofocus: false, + this.autoFocus: false, this.focusNode, this.textDirection: TextDirection.ltr, this.keyboardType: TextInputType.number, @@ -235,15 +233,15 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi width: _width, height: widget.pinBoxHeight, child: TextField( - autofocus: !kIsWeb ? widget.autofocus : false, + autofocus: !kIsWeb ? widget.autoFocus : false, enableInteractiveSelection: false, focusNode: focusNode, controller: widget.controller, keyboardType: widget.keyboardType, inputFormatters: widget.keyboardType == TextInputType.number ? [FilteringTextInputFormatter.digitsOnly] : null, style: TextStyle( - height: 0.1, color: Colors.transparent, -// color: Colors.transparent, + height: 0.1, + color: Colors.transparent, ), decoration: InputDecoration( contentPadding: EdgeInsets.all(0), diff --git a/pubspec.yaml b/pubspec.yaml index 18a19ac..af21aaf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: cupertino_icons: ^1.0.2 flutter_svg: ^0.19.3 fluttertoast: ^7.1.8 + http: ^0.13.0 dev_dependencies: flutter_test: