elCaribe app - customization and branding
This commit is contained in:
7
news-app/lib/utils/ErrorMessageKeys.dart
Normal file
7
news-app/lib/utils/ErrorMessageKeys.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
class ErrorMessageKeys {
|
||||
static const String defaultErrorMessage = "Something went wrong. Please try again later!";
|
||||
static const String noInternet = "No internet";
|
||||
static const String serverDownMessage = "Our service is temporarily unavailable due to maintenance or high traffic. Please check back in a little while.";
|
||||
static const String noDataMessage = "No Data Found";
|
||||
static const String requestAgainMessage = "The Request cannot be fulfilled for now.\nPlease Try again later";
|
||||
}
|
||||
165
news-app/lib/utils/api.dart
Normal file
165
news-app/lib/utils/api.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:news/data/repositories/Auth/authLocalDataSource.dart';
|
||||
import 'package:news/utils/ErrorMessageKeys.dart';
|
||||
import 'package:news/utils/internetConnectivity.dart';
|
||||
import 'package:news/utils/constant.dart';
|
||||
|
||||
class ApiMessageAndCodeException implements Exception {
|
||||
final String errorMessage;
|
||||
|
||||
ApiMessageAndCodeException({required this.errorMessage});
|
||||
|
||||
Map toError() => {"message": errorMessage};
|
||||
|
||||
@override
|
||||
String toString() => errorMessage;
|
||||
}
|
||||
|
||||
class ApiException implements Exception {
|
||||
String errorMessage;
|
||||
|
||||
ApiException(this.errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
class Api {
|
||||
static String getToken() {
|
||||
String token = AuthLocalDataSource().getJWTtoken();
|
||||
return (token.trim().isNotEmpty) ? token : "";
|
||||
}
|
||||
|
||||
static Map<String, String> get headers => {"Authorization": 'Bearer ${getToken()}'};
|
||||
|
||||
//all apis list
|
||||
static String getUserSignUpApi = 'user_signup';
|
||||
static String getNewsApi = 'get_news';
|
||||
static String getSettingApi = 'get_settings';
|
||||
static String getCatApi = 'get_category';
|
||||
static String setBookmarkApi = 'set_bookmark';
|
||||
static String getBookmarkApi = 'get_bookmark';
|
||||
static String setCommentApi = 'set_comment';
|
||||
static String getCommentByNewsApi = 'get_comment_by_news';
|
||||
static String getBreakingNewsApi = 'get_breaking_news';
|
||||
static String setUpdateProfileApi = 'update_profile';
|
||||
static String setRegisterToken = 'register_token';
|
||||
static String setUserCatApi = 'set_user_category';
|
||||
static String getUserByIdApi = 'get_user_by_id';
|
||||
static String setCommentDeleteApi = 'delete_comment';
|
||||
static String setLikesDislikesApi = 'set_like_dislike';
|
||||
static String setFlagApi = 'set_flag';
|
||||
static String getLiveStreamingApi = 'get_live_streaming';
|
||||
static String getSubCategoryApi = 'get_subcategory_by_category';
|
||||
static String setLikeDislikeComApi = 'set_comment_like_dislike';
|
||||
static String deleteUserNotiApi = 'delete_user_notification';
|
||||
static String getQueApi = 'get_question';
|
||||
static String getQueResultApi = 'get_question_result';
|
||||
static String setQueResultApi = 'set_question_result';
|
||||
static String userDeleteApi = 'delete_user';
|
||||
static String getTagsApi = 'get_tag';
|
||||
static String setNewsApi = 'set_news';
|
||||
static String setDeleteNewsApi = 'delete_news';
|
||||
static String setDeleteImageApi = 'delete_news_images';
|
||||
static String getVideosApi = 'get_videos';
|
||||
static String getLanguagesApi = 'get_languages_list';
|
||||
static String getLangJsonDataApi = 'get_language_json_data';
|
||||
static String getPagesApi = 'get_pages';
|
||||
static String getPolicyPagesApi = 'get_policy_pages';
|
||||
static String getFeatureSectionApi = 'get_featured_sections';
|
||||
static String getLikeNewsApi = 'get_like';
|
||||
static String setNewsViewApi = 'set_news_view';
|
||||
static String setBreakingNewsViewApi = 'set_breaking_news_view';
|
||||
static String getAdsNewsDetailsApi = 'get_ad_space_news_details';
|
||||
static String getLocationCityApi = 'get_location';
|
||||
static String slugCheckApi = 'check_slug_availability';
|
||||
static String rssFeedApi = 'get_rss_feed';
|
||||
static String becomeAnAuthorApi = 'become_author';
|
||||
static String getAuthorNewsApi = 'get_authors_news';
|
||||
static String getDraftNewsApi = 'get_user_drafted_news';
|
||||
static String geminiMetaInfoApi = 'https://generativelanguage.googleapis.com/v1beta/models/';
|
||||
|
||||
static FormData? toFormData(dynamic data) {
|
||||
if (data == null) return null;
|
||||
|
||||
if (data is Map<String, dynamic>) {
|
||||
return FormData.fromMap(data, ListFormat.multiCompatible);
|
||||
}
|
||||
|
||||
if (data is List) {
|
||||
final formData = FormData();
|
||||
for (var value in data) {
|
||||
formData.fields.add(MapEntry("list[]", value.toString()));
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
|
||||
// Raw value (int/string/etc.) → cannot convert → return null
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> sendApiRequest({required dynamic body, required String url, bool isGet = false}) async {
|
||||
try {
|
||||
if (!await InternetConnectivity.isNetworkAvailable()) {
|
||||
throw const SocketException(ErrorMessageKeys.noInternet);
|
||||
}
|
||||
|
||||
final Dio dio = Dio();
|
||||
final apiUrl = "$databaseUrl$url";
|
||||
|
||||
// Auto-detect GET for endpoints starting with "get_"
|
||||
final shouldUseGet = isGet || url.startsWith('get_');
|
||||
|
||||
// Convert body to query parameters for GET requests
|
||||
Map<String, dynamic>? queryParams;
|
||||
if (shouldUseGet && body != null && body is Map<String, dynamic>) {
|
||||
queryParams = body;
|
||||
}
|
||||
|
||||
// Convert only if possible (for POST)
|
||||
final convertedBody = toFormData(body);
|
||||
|
||||
final response = (shouldUseGet)
|
||||
? await dio.get(
|
||||
apiUrl,
|
||||
queryParameters: queryParams ?? {},
|
||||
options: Options(headers: headers),
|
||||
)
|
||||
: await dio.post(
|
||||
apiUrl,
|
||||
data: convertedBody ?? body,
|
||||
options: Options(headers: headers),
|
||||
);
|
||||
|
||||
if (response.data['error'] == 'true') {
|
||||
throw ApiException(response.data['message']);
|
||||
}
|
||||
|
||||
return Map<String, dynamic>.from(response.data);
|
||||
} on DioException catch (e) {
|
||||
print('DEBUG API ERROR: ${e.response?.statusCode} - ${e.message} - URL: $url');
|
||||
print('DEBUG API RESPONSE: ${e.response?.data}');
|
||||
if (e.response?.statusCode == 503) {
|
||||
throw ApiException(ErrorMessageKeys.serverDownMessage);
|
||||
} else if (e.response?.statusCode == 404) {
|
||||
throw ApiException(ErrorMessageKeys.requestAgainMessage);
|
||||
}
|
||||
|
||||
throw ApiException(
|
||||
e.error is SocketException ? ErrorMessageKeys.noInternet : ErrorMessageKeys.defaultErrorMessage,
|
||||
);
|
||||
} on SocketException catch (e) {
|
||||
print('DEBUG SOCKET ERROR: ${e.message}');
|
||||
throw SocketException(e.message);
|
||||
} on ApiException catch (e) {
|
||||
print('DEBUG API EXCEPTION: ${e.errorMessage}');
|
||||
throw ApiException(e.errorMessage);
|
||||
} catch (e) {
|
||||
print('DEBUG CATCH ALL ERROR: $e');
|
||||
throw ApiException(ErrorMessageKeys.defaultErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
244
news-app/lib/utils/appLanguages.dart
Normal file
244
news-app/lib/utils/appLanguages.dart
Normal file
@@ -0,0 +1,244 @@
|
||||
const Map<String, dynamic> appLanguageLabelKeys = {
|
||||
"somethingMSg": "Something went wrong. Please try again after some time",
|
||||
"bookmarkLbl": "Bookmarks",
|
||||
"loginLbl": "Login",
|
||||
"welTitle1": "Always Up-to-Date",
|
||||
"welTitle2": "Bookmark & Share",
|
||||
"welTitle3": "New Categories",
|
||||
"welDes1": "Receive notifications for the most recent news updates and many more.",
|
||||
"welDes2": "Save and easily share news with your friends using our intuitive news app feature.",
|
||||
"welDes3": "Enjoy expertly tailored news, crafted exclusively for your interests.",
|
||||
"nameLbl": "Name",
|
||||
"emailLbl": "Email",
|
||||
"passLbl": "Password",
|
||||
"confpassLbl": "Confirm Password",
|
||||
"priPolicy": "Privacy Policy",
|
||||
"andLbl": " and ",
|
||||
"termLbl": "Terms of Service",
|
||||
"forgotPassLbl": "Forgot Password ?",
|
||||
"internetmsg": "Internet Connection not available",
|
||||
"loginMsg": "Login Successfully",
|
||||
"loginNowLbl": "Login Now",
|
||||
"logoutLbl": "Logout",
|
||||
"cancelBtn": "Cancel",
|
||||
"noNews": "News Not Available",
|
||||
"exitWR": "Double tap back button to exit",
|
||||
"shareLbl": "Share",
|
||||
"deactiveMsg": "You are deactivated by admin",
|
||||
"bookmarkNotAvail": "Bookmarks Not Available",
|
||||
"notiNotAvail": "Notifications Not Available",
|
||||
"notificationLbl": "Notifications",
|
||||
"logoutTxt": "Are you sure you want to Logout?",
|
||||
"yesLbl": "Yes",
|
||||
"noLbl": "No",
|
||||
"frgtPassHead": "Enter the email address associated with your account",
|
||||
"forgotPassSub": "We will email you a link to reset your password",
|
||||
"submitBtn": "Submit",
|
||||
"verifyEmailMsg": "Please first verify your email address!!!",
|
||||
"passReset": "Password reset link has been sent to your mail",
|
||||
"profileUpdateMsg": "Profile Data Updated Successfully",
|
||||
"bookmarkLogin": "Please Login to Access Your Bookmarks !!",
|
||||
"preferenceSave": "Your preference saved!!",
|
||||
"managePreferences": "Manage Preferences",
|
||||
"loginReqMsg": "Login Required...",
|
||||
"firstFillData": "Please First Fill Data...!",
|
||||
"deleteTxt": "Delete",
|
||||
"reportTxt": "Report",
|
||||
"nameRequired": "Name is Required",
|
||||
"nameLength": "Name should be atleast 2 character long",
|
||||
"emailRequired": "email address is Required",
|
||||
"emailValid": "Please enter a valid email Address!",
|
||||
"pwdRequired": "Password is Required",
|
||||
"confPassRequired": "Confirm Password is Required",
|
||||
"confPassNotMatch": "Confirm Password not match",
|
||||
"photoLibLbl": "Photo Library",
|
||||
"cameraLbl": "Camera",
|
||||
"verifSentMail": "Verification email sent to ",
|
||||
"cancelLogin": "Login cancelled by the user.",
|
||||
"loginTxt": "Log In",
|
||||
"loginBtn": "Login",
|
||||
"signupBtn": "Sign Up",
|
||||
"otpVerifyLbl": "OTP Verification",
|
||||
"enterMblLbl": "Enter Your Mobile Number",
|
||||
"receiveDigitLbl": "You'll Receive 6 digit code for phone number verification",
|
||||
"reqOtpLbl": "Request OTP",
|
||||
"otpSentLbl": "OTP has been sent to ",
|
||||
"resendCodeLbl": "Resend Code in",
|
||||
"mobileLbl": "Mobile",
|
||||
"darkModeLbl": "Dark Mode",
|
||||
"changeLang": "Change Language",
|
||||
"rateUs": "Rate Us",
|
||||
"shareApp": "Share App",
|
||||
"weatherLbl": "Weather Forecast",
|
||||
"categoryLbl": "Categories",
|
||||
"allLbl": "All",
|
||||
"comLbl": "Comment ",
|
||||
"saveLbl": "Save",
|
||||
"txtSizeLbl": "Text Size",
|
||||
"speakLoudLbl": "Speak Loud",
|
||||
"likeLbl": "likes",
|
||||
"comsLbl": "Comments",
|
||||
"shareThoghtLbl": "Share Your Thoughts.",
|
||||
"repliesLbl": "Replies",
|
||||
"publicReply": "Add a public reply...",
|
||||
"personalLbl": "Personal",
|
||||
"newsLbl": "News",
|
||||
"plzLbl": "Please",
|
||||
"fastTrendNewsLbl": "Porque Mereces Verdaderas Respuestas! ",
|
||||
"enterOtpTxt": "Please Enter OTP",
|
||||
"otpError": "Error validating OTP, try again",
|
||||
"otpMsg": "OTP verified successfully",
|
||||
"resendLbl": "Resend OTP",
|
||||
"otpTimeoutLbl": "Otp Retrieval Timeout!!!",
|
||||
"mblRequired": "Mobile number is Required",
|
||||
"mblValid": "Please enter a valid mobile number!",
|
||||
"codeSent": "Code Sent Successfully!!!",
|
||||
"relatedNews": "You might also like",
|
||||
"optSel": "Please Select One Option!!!",
|
||||
"madeBy": "Made by",
|
||||
"skip": "Skip",
|
||||
"nxt": "Next",
|
||||
"signInTab": "Sign In",
|
||||
"agreeTermPolicyLbl": "By Logging In, you agree to our",
|
||||
"addTCFirst": "Please Ask Admin to Add Privacy Policy & Terms and Conditions first !!",
|
||||
"orLbl": "or Log In with",
|
||||
"signupDescr": "Create\nan Account",
|
||||
"firstAccLbl": "First to access",
|
||||
"allFunLbl": "all Functions",
|
||||
"chooseLanLbl": "Select Language",
|
||||
"videosLbl": "Videos",
|
||||
"search": "Search",
|
||||
"searchHomeNews": "Search News, Categories, etc.",
|
||||
"viewMore": "View More",
|
||||
"viewFullCoverage": "View full Coverage",
|
||||
"updateName": "Update your Name",
|
||||
"loginDescr": "Let's Sign \nYou In",
|
||||
"logoutAcc": "Logout Account",
|
||||
"deleteAcc": "Delete Account",
|
||||
"deleteAlertTitle": "Re-Login",
|
||||
"deleteRelogin": "To Delete your Account, You need to Login again.\nAfter that you will be able to Delete your Account.",
|
||||
"deleteConfirm": "Are you sure?\nDo You Really Want to Delete Your Account?",
|
||||
"pwdLength": "Password should be more than 6 character long",
|
||||
"userNotFound": "No user found for given email.",
|
||||
"wrongPassword": "Wrong password provided for that user.",
|
||||
"weakPassword": "The password provided is too weak.",
|
||||
"emailAlreadyInUse": "The account already exists for that email.",
|
||||
"invalidPhoneNumber": "The provided phone number is not valid.",
|
||||
"invalidVerificationCode": "The sms verification code used to create the phone auth credential is invalid.",
|
||||
"ago": "ago",
|
||||
"years": "years",
|
||||
"months": "months",
|
||||
"minutes": "minutes",
|
||||
"seconds": "seconds",
|
||||
"hours": "hours",
|
||||
"days": "days",
|
||||
"justNow": "just now",
|
||||
"about": "about",
|
||||
"liveVideosLbl": "Live Videos",
|
||||
"stdPostLbl": "Standard Post",
|
||||
"videoYoutubeLbl": "Video (Youtube)",
|
||||
"videoOtherUrlLbl": "Video (Other Url)",
|
||||
"videoUploadLbl": "Video (Upload)",
|
||||
"createNewsLbl": "Create News",
|
||||
"step1Of2Lbl": "Step 1 of 2",
|
||||
"catLbl": "Category",
|
||||
"plzSelCatLbl": "Please select category",
|
||||
"subcatLbl": "SubCategory",
|
||||
"contentTypeLbl": "Content Type",
|
||||
"uploadVideoLbl": "Upload Video",
|
||||
"youtubeUrlLbl": "Youtube Url",
|
||||
"otherUrlLbl": "Other Url",
|
||||
"selContentTypeLbl": "Select Content Type",
|
||||
"titleLbl": "Title",
|
||||
"tagLbl": "Tag",
|
||||
"showTilledDate": "Show Till Date",
|
||||
"uploadMainImageLbl": "Upload Main Image",
|
||||
"uploadOtherImageLbl": "Upload Other Image",
|
||||
"plzUploadVideoLbl": "Please upload video!!!",
|
||||
"plzAddMainImageLbl": "Please add main image!!!",
|
||||
"selTagLbl": "Select Tag",
|
||||
"selSubCatLbl": "Select Sub Category",
|
||||
"selCatLbl": "Select Category",
|
||||
"editNewsLbl": "Edit News",
|
||||
"doYouReallyNewsLbl": "Do You Really Want to Delete this News?",
|
||||
"delNewsLbl": "Delete News",
|
||||
"newsTitleReqLbl": "News title is required!!!",
|
||||
"plzAddValidTitleLbl": "Please add valid news title!!!",
|
||||
"urlReqLbl": "Url is required!!!",
|
||||
"plzValidUrlLbl": "Please add valid url!!!",
|
||||
"manageNewsLbl": "Manage News",
|
||||
"step2of2Lbl": "Step 2 of 2",
|
||||
"descLbl": "Description",
|
||||
"RetryLbl": "Retry",
|
||||
"previewLbl": "Preview",
|
||||
"sponsoredLbl": "Sponsored",
|
||||
"searchForLbl": "Search Result for",
|
||||
"readLessLbl": "Read less",
|
||||
"readMoreLbl": "Read more",
|
||||
"myProfile": "My Profile",
|
||||
"editProfile": "Edit Profile",
|
||||
"noComments": "Be the First One to Comment !!!",
|
||||
"minute": "minute",
|
||||
"read": "read",
|
||||
"selLocationLbl": "Select Location",
|
||||
"metaKeywordLbl": "Meta Keyword",
|
||||
"metaTitleLbl": "Meta Title",
|
||||
"metaDescriptionLbl": "Meta Description",
|
||||
"slugLbl": "Slug",
|
||||
"metaTitleWarningLbl": "Meta Title length should not exceed 60 characters.",
|
||||
"metaDescriptionWarningLbl": "Meta Description length should between 50 to 160 characters.",
|
||||
"metaKeywordWarningLbl": "Meta Keywords are not more than 10 keyword phrases & should be comma separated.",
|
||||
"slugWarningLbl": "Slug only accept lowercase letters, numbers, and hyphens. No spaces or special characters allowed.",
|
||||
"metaTitleRequired": "Meta title is Required",
|
||||
"metaDescriptionRequired": "Meta Description is Required",
|
||||
"metaKeywordRequired": "Meta Keyword is Required",
|
||||
"slugRequired": "Slug is Required",
|
||||
"slugValid": "Please enter valid Slug!",
|
||||
"slugUsedAlready": "This slug is already in use. Please add any other slug.",
|
||||
"maintenanceMessageLbl": "We're Under Maintenance\nPlease try again later.",
|
||||
"notificationLogin": "Please Login to view Your Notifications !!",
|
||||
"publishDate": "Publish Date",
|
||||
"dateConfirmation": "Please change Show till date , as it can't be set before Publish date",
|
||||
"expiredKey": "expired",
|
||||
"deactivatedKey": "deactivated",
|
||||
"clearFilter": "Clear filter",
|
||||
"FilterBy": "Filter by",
|
||||
"rssFeed": "RSS Feed",
|
||||
"profile": "Profile",
|
||||
"homeLbl": "Home",
|
||||
"replyLbl": "Reply",
|
||||
"disabledCommentsMsg": "Comments are disabled for this News by admin",
|
||||
"last": "Last",
|
||||
"today": "Today",
|
||||
"clear": "Clear",
|
||||
"apply": "Apply",
|
||||
"date": "Date",
|
||||
"continueWith": "Continue with",
|
||||
"google": "Google",
|
||||
"apple": "Apple",
|
||||
"fb": "Facebook",
|
||||
"didntGetCode": "Didn't get Code?",
|
||||
"viewsLbl": "Views",
|
||||
"recentVidLbl": "Recent News Videos",
|
||||
"forceUpdateTitleLbl": "Update Required",
|
||||
"newVersionAvailableTitleLbl": "New Version Available !!!",
|
||||
"newVersionAvailableDescLbl": "Would you like to update now ?",
|
||||
"forceUpdateDescLbl": "A new version of this app is available with important improvements and features. To continue using the app, please update to the latest version.",
|
||||
"exitLbl": "Exit",
|
||||
"newsCreatedSuccessfully": "Your news has been created successfully! It will be visible to others after admin approval.",
|
||||
"summarizedDescription": "Summarized Description",
|
||||
"clickSummarizeDescription": "Click 'Summarize Description' to generate a summary of your content",
|
||||
"enterDescriptionFirst": "Add description First to generate Summarized Description",
|
||||
"authorReviewPendingLbl": "Pending Review",
|
||||
"becomeAuthorLbl": "Become an Author",
|
||||
"authorLbl": "Author",
|
||||
"saveAsDraftLbl": "Save As Draft",
|
||||
"manageNewsAllLbl": "All News",
|
||||
"manageNewsDraftLbl": "Draft News",
|
||||
"publishBtnLbl": "Publish",
|
||||
"addYourBioHintLbl": "Add your bio",
|
||||
"addLinkHereHintLbl": "Add {type} Link here",
|
||||
"noteForAuthorLbl": "You can add social media links & Bio only if you are the author.",
|
||||
"socialMediaLinksLbl": "Social media links",
|
||||
"followLbl": "Follow :"
|
||||
};
|
||||
38
news-app/lib/utils/constant.dart
Normal file
38
news-app/lib/utils/constant.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
//Please add your admin panel url here and make sure you do not add '/' at the end of the url
|
||||
|
||||
const String baseUrl = "https://news.ajsmartsolutions.space";
|
||||
|
||||
const String databaseUrl = "$baseUrl/api/"; //Do not change
|
||||
|
||||
const String shareNavigationWebUrl = "https://newspaper.ajsmartsolutions.space"; //with http:// OR https://
|
||||
|
||||
const int limitOfSectionsData = 5;
|
||||
|
||||
const int limitOfAPIData = 10;
|
||||
const int limitOfStyle1 = 3;
|
||||
const int limitOfAllOtherStyle = 20;
|
||||
|
||||
//Facebook Login enable/disable
|
||||
const bool fblogInEnabled = false;
|
||||
|
||||
//set value for survey show after news data
|
||||
const int surveyShow = 4;
|
||||
|
||||
//set value for native ads show after news data
|
||||
const int nativeAdsIndex = 3;
|
||||
|
||||
//set value for interstitial ads show after news data
|
||||
const int interstitialAdsIndex = 2;
|
||||
|
||||
//set value for reward ads show after news data
|
||||
const int rewardAdsIndex = 4;
|
||||
|
||||
const String appName = 'elCaribe';
|
||||
|
||||
enum VideoViewType { normal, page }
|
||||
|
||||
enum ContentType { youtube, uploaded, live }
|
||||
|
||||
enum AuthorStatus { approved, pending, rejected }
|
||||
|
||||
enum AuthorLayoutType { list, grid }
|
||||
39
news-app/lib/utils/hiveBoxKeys.dart
Normal file
39
news-app/lib/utils/hiveBoxKeys.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
//Authbox keys
|
||||
const String authBoxKey = "authBox";
|
||||
const String jwtTokenKey = "jwtToken";
|
||||
const String isLogInKey = "isLogIn";
|
||||
const String userIdKey = "userId";
|
||||
const String userNameKey = "userName";
|
||||
const String userMobKey = "userMob";
|
||||
const String userEmailKey = "userEmail";
|
||||
const String userProfileKey = "userProfile";
|
||||
const String userRoleKey = "userRole";
|
||||
const String userStatusKey = "userStatus";
|
||||
const String userTypeKey = "Type";
|
||||
const String authorWhatsappLinkKey = "whatsAppLink";
|
||||
const String authorTelegramLinkKey = "telegramLink";
|
||||
const String authorfacebookLinkKey = "facebookLink";
|
||||
const String authorLinkedInLinkKey = "linkedInLink";
|
||||
const String authorBioKey = "authorBio";
|
||||
const String authorStatusKey = "authorStatus";
|
||||
|
||||
//Settings box keys
|
||||
const String settingsBoxKey = "settings";
|
||||
const String currentLanguageCodeKey = "currentLanguageCode";
|
||||
const String currentLanguageIDKey = "currentLanguageId";
|
||||
const String currentLanguageRTLKey = "currentLanguageRTL";
|
||||
const String introSliderKey = "introSlider";
|
||||
const String currentThemeKey = "currentTheme";
|
||||
const String notificationKey = "notification";
|
||||
const String tokenKey = "token";
|
||||
const String historyListKey = "historyList";
|
||||
const String notificationEnabledKey = "notificationEnabled";
|
||||
|
||||
//Location box keys
|
||||
const String locationCityBoxKey = "locationCity";
|
||||
const String latitudeKey = "Latitude";
|
||||
const String longitudeKey = "Longitude";
|
||||
|
||||
///VideoPreference
|
||||
const String videoPreferenceKey = "videoPreference";
|
||||
const String videoStyle = 'videoStyle';
|
||||
13
news-app/lib/utils/internetConnectivity.dart
Normal file
13
news-app/lib/utils/internetConnectivity.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
|
||||
class InternetConnectivity {
|
||||
static Future<bool> isNetworkAvailable() async {
|
||||
final List<ConnectivityResult> connectivityResult = await Connectivity().checkConnectivity();
|
||||
if (connectivityResult.contains(ConnectivityResult.mobile)) {
|
||||
return true;
|
||||
} else if (connectivityResult.contains(ConnectivityResult.wifi)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
2
news-app/lib/utils/labelKeys.dart
Normal file
2
news-app/lib/utils/labelKeys.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
const String lightThemeKey = "lightTheme";
|
||||
const String darkThemeKey = "darkTheme";
|
||||
165
news-app/lib/utils/strings.dart
Normal file
165
news-app/lib/utils/strings.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
const String ID = "id";
|
||||
const String DATA = "data";
|
||||
const String NAME = "name";
|
||||
const String EMAIL = "email";
|
||||
const String TYPE = "type";
|
||||
const String URL = "url";
|
||||
const String STATUS = "status";
|
||||
const String FCM_ID = "fcm_id";
|
||||
const String PROFILE = "profile";
|
||||
const String ROLE = "role";
|
||||
const String MOBILE = "mobile";
|
||||
const String FIREBASE_ID = "firebase_id";
|
||||
const String CATEGORY_ID = "category_id";
|
||||
const String CONTENT_TYPE = "content_type";
|
||||
const String SHOW_TILL = "show_till";
|
||||
const String CONTENT_VALUE = "content_value";
|
||||
const String DATE = "date";
|
||||
const String TITLE = "title";
|
||||
const String DESCRIPTION = "description";
|
||||
const String IMAGE = "image";
|
||||
const String CATEGORY_NAME = "category_name";
|
||||
const String NEWS_ID = "news_id";
|
||||
const String BR_NEWS_ID = "breaking_news_id";
|
||||
const String USER_ID = "user_id";
|
||||
const String USER = "user";
|
||||
const String OFFSET = "offset";
|
||||
const String LIMIT = "limit";
|
||||
const String USER_NEWS = "get_user_news";
|
||||
const String MESSAGE = "message";
|
||||
const String COUNTER = "counter";
|
||||
const String DATE_SENT = "date_sent";
|
||||
const String OTHER_IMAGE = "other_image";
|
||||
const String IMAGES = "images";
|
||||
const String IMAGE_DATA = "image_data";
|
||||
const String COMMENT_ID = "comment_id";
|
||||
const String TOTAL_LIKE = "total_like";
|
||||
const String LIKE = "like";
|
||||
const String BOOKMARK = "bookmark";
|
||||
const String SUBCAT_NAME = "subcategory_name";
|
||||
const String SUBCAT_ID = "subcategory_id";
|
||||
const String DISLIKE = "dislike";
|
||||
const String TOTAL_DISLIKE = "total_dislike";
|
||||
const String PARENT_ID = "parent_id";
|
||||
const String REPLY = "reply";
|
||||
const String SEARCH = "search";
|
||||
const String TAGNAME = "tag_name";
|
||||
const String CATEGORY = "category";
|
||||
const String SUBCATEGORY = "sub_category";
|
||||
const String SUBCATEGORIES = "sub_categories";
|
||||
const String TAG_ID = "tag_id";
|
||||
const String TAG = "tag_name";
|
||||
const String QUESTION = "question";
|
||||
const String QUESTION_ID = "question_id";
|
||||
const String OPTION_ID = "option_id";
|
||||
const String OPTION = "survey_options";
|
||||
const String OPTIONS = "options";
|
||||
const String PERCENTAGE = "percentage";
|
||||
const String CATEGORY_MODE = "category_mode";
|
||||
const String BREAK_NEWS_MODE = "breaking_news_mode";
|
||||
const String COMM_MODE = "comments_mode";
|
||||
const String LIVE_STREAM_MODE = "live_streaming_mode";
|
||||
const String SUBCAT_MODE = "subcategory_mode";
|
||||
const String GO_REWARDED_ID = "google_rewarded_video_id";
|
||||
const String GO_INTER_ID = "google_interstitial_id";
|
||||
const String GO_BANNER_ID = "google_banner_id";
|
||||
const String GO_NATIVE_ID = "google_native_unit_id";
|
||||
const String IOS_GO_REWARDED_ID = "ios_google_rewarded_video_id";
|
||||
const String IOS_GO_INTER_ID = "ios_google_interstitial_id";
|
||||
const String IOS_GO_BANNER_ID = "ios_google_banner_id";
|
||||
const String IOS_GO_NATIVE_ID = "ios_google_native_unit_id";
|
||||
const String U_REWARDED_ID = "unity_rewarded_video_id";
|
||||
const String U_INTER_ID = "unity_interstitial_id";
|
||||
const String U_BANNER_ID = "unity_banner_id";
|
||||
const String U_AND_GAME_ID = "android_game_id";
|
||||
const String IOS_U_REWARDED_ID = "ios_unity_rewarded_video_id";
|
||||
const String IOS_U_INTER_ID = "ios_unity_interstitial_id";
|
||||
const String IOS_U_BANNER_ID = "ios_unity_banner_id";
|
||||
const String IOS_U_GAME_ID = "ios_game_id";
|
||||
const String ADS_MODE = "in_app_ads_mode";
|
||||
const String IOS_ADS_MODE = "ios_in_app_ads_mode";
|
||||
const String ADS_TYPE = "ads_type";
|
||||
const String IOS_ADS_TYPE = "ios_ads_type";
|
||||
const String LOCATION_WISE_NEWS_MODE = "location_news_mode";
|
||||
const String WEATHER_MODE = "weather_mode";
|
||||
const String MAINTENANCE_MODE = "maintenance_mode";
|
||||
const String LOCATION = "location";
|
||||
const String LOCATION_ID = "location_id";
|
||||
const String LOCATION_NAME = "location_name";
|
||||
const String LATITUDE = "latitude";
|
||||
const String LONGITUDE = "longitude";
|
||||
const String TOKEN = "token";
|
||||
const String LANGUAGE = "language";
|
||||
const String CODE = "code";
|
||||
const String DEFAULT_LANG = "default_language";
|
||||
const String PAGE_CONTENT = "page_content";
|
||||
const String ISRTL = "isRTL";
|
||||
const String LANGUAGE_ID = "language_id";
|
||||
const String SECTION_ID = "section_id";
|
||||
const String PAGE_ICON = "page_icon";
|
||||
const String NEWS = "news";
|
||||
const String BREAKING_NEWS = "breaking_news";
|
||||
const String VIDEOS = "videos";
|
||||
const String SHORT_DESC = "short_description";
|
||||
const String NEWS_TYPE = "news_type";
|
||||
const String VIDEOS_TYPE = "videos_type";
|
||||
const String FILTER_TYPE = "filter_type";
|
||||
const String CAT_IDS = "category_ids";
|
||||
const String SUBCAT_IDS = "subcategory_ids";
|
||||
const String NEWS_IDS = "news_ids";
|
||||
const String STYLE_APP = "style_app";
|
||||
const String NEWS_TOTAL = "news_total";
|
||||
const String TOTAL = "total";
|
||||
const String BREAK_NEWS_TOTAL = "breaking_news_total";
|
||||
const String VIDEOS_TOTAL = "videos_total";
|
||||
const String IS_LOGIN = "is_login";
|
||||
const String TOTAL_VIEWS = "total_views";
|
||||
const String DISPLAY_NAME_LANG = "display_name";
|
||||
const String AD_SPACES = "ad_spaces";
|
||||
const String AD_SPACE = "ad_space";
|
||||
const String AD_FEATURED_SECTION_ID = "ad_featured_section_id";
|
||||
const String AD_IMAGE = "ad_image";
|
||||
const String AD_URL = "ad_url";
|
||||
const String ACTION_TYPE = "action_type";
|
||||
const String CONTENT_DATA = "content_data";
|
||||
const String META_TITLE = "meta_title";
|
||||
const String META_DESC = "meta_description";
|
||||
const String META_KEYWORD = "meta_keyword";
|
||||
const String SLUG = "slug";
|
||||
const String ERROR = "error";
|
||||
const String PUBLISHED_DATE = "published_date";
|
||||
const String IS_EXPIRED = "is_expired";
|
||||
const String FEED_NAME = "feed_name";
|
||||
const String FEED_URL = "feed_url";
|
||||
const String RSS_FEED_MODE = "rss_feed_mode";
|
||||
const String MOBILE_LOGIN_MODE = "mobile_login_mode";
|
||||
const String COUNTRY_CODE = "country_code";
|
||||
const String SHARE_APP_TEXT = "shareapp_text";
|
||||
const String APPSTORE_ID = "app_store_id";
|
||||
const String ANDROID_APP_LINK = "android_app_link";
|
||||
const String IOS_APP_LINK = "ios_app_link";
|
||||
const String WEB_SETTING = "web_setting";
|
||||
const String MERGE_TAG = "merge_tag";
|
||||
const String IS_COMMENT_ENABLED = "is_comment";
|
||||
const String LAST_N_DAYS = "last_n_days";
|
||||
const String YEAR = "year";
|
||||
const String VIDEO_TYPE_PREFERENCE = "video_type_preference";
|
||||
const String SOURCE_TYPE = "source_type";
|
||||
const String SECTION_OFFSET = "section_offset";
|
||||
const String SECTION_LIMIT = "section_limit";
|
||||
const String UPDATED_DATE = "updated_at";
|
||||
const String FORCE_UPDT_APP_MODE = 'force_update_app_mode';
|
||||
const String ANDROID_APP_VERSION = 'android_app_version';
|
||||
const String IOS_APP_VERSION = 'ios_app_version';
|
||||
const String SUMM_DESCRIPTION = 'summarized_description';
|
||||
const String GEMINI_API_KEY = 'google_gemini_api_key';
|
||||
const String IS_DRAFT_KEY = 'is_draft';
|
||||
const String IS_AUTHOR = 'is_author';
|
||||
const String AUTHOR = 'author';
|
||||
const String AUTHOR_BIO = 'bio';
|
||||
const String AUTHOR_STATUS = "author_status";
|
||||
const String AUTHOR_WHATSAPP_LINK = 'whatsapp_link';
|
||||
const String AUTHOR_TELEGRAM_LINK = 'telegram_link';
|
||||
const String AUTHOR_FACEBOOK_LINK = 'facebook_link';
|
||||
const String AUTHOR_LINKEDIN_LINK = 'linkedin_link';
|
||||
const String AUTHOR_ID = "author_id";
|
||||
507
news-app/lib/utils/uiUtils.dart
Normal file
507
news-app/lib/utils/uiUtils.dart
Normal file
@@ -0,0 +1,507 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:news/app/routes.dart';
|
||||
import 'package:news/cubits/Auth/authCubit.dart';
|
||||
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
|
||||
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
|
||||
import 'package:news/cubits/appLocalizationCubit.dart';
|
||||
import 'package:news/cubits/appSystemSettingCubit.dart';
|
||||
import 'package:news/cubits/languageJsonCubit.dart';
|
||||
import 'package:news/ui/screens/NewsDetail/Widgets/InterstitialAds/googleInterstitialAds.dart';
|
||||
import 'package:news/ui/screens/NewsDetail/Widgets/InterstitialAds/unityInterstitialAds.dart';
|
||||
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
|
||||
import 'package:news/ui/styles/colors.dart';
|
||||
import 'package:news/ui/widgets/SnackBarWidget.dart';
|
||||
import 'package:news/ui/widgets/customTextLabel.dart';
|
||||
import 'package:news/utils/constant.dart';
|
||||
import 'package:news/utils/labelKeys.dart';
|
||||
import 'package:news/ui/styles/appTheme.dart';
|
||||
import 'package:news/utils/hiveBoxKeys.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class UiUtils {
|
||||
static GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
static Future<void> setDynamicStringValue(String key, String value) async {
|
||||
Hive.box(settingsBoxKey).put(key, value);
|
||||
}
|
||||
|
||||
static Future<void> setDynamicListValue(String key, String value) async {
|
||||
List<String>? valueList = getDynamicListValue(key);
|
||||
if (!valueList.contains(value)) {
|
||||
if (valueList.length > 4) valueList.removeAt(0);
|
||||
valueList.add(value);
|
||||
|
||||
Hive.box(settingsBoxKey).put(key, valueList);
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> getDynamicListValue(String key) {
|
||||
return Hive.box(settingsBoxKey).get(key) ?? [];
|
||||
}
|
||||
|
||||
static String getSvgImagePath(String imageName) {
|
||||
return "assets/images/svgImage/$imageName.svg";
|
||||
}
|
||||
|
||||
static String getPlaceholderPngPath() {
|
||||
return "assets/images/placeholder.png";
|
||||
}
|
||||
|
||||
static ColorScheme getColorScheme(BuildContext context) {
|
||||
return Theme.of(context).colorScheme;
|
||||
}
|
||||
|
||||
// get app theme
|
||||
static String getThemeLabelFromAppTheme(AppTheme appTheme) {
|
||||
if (appTheme == AppTheme.Dark) {
|
||||
return darkThemeKey;
|
||||
}
|
||||
return lightThemeKey;
|
||||
}
|
||||
|
||||
static AppTheme getAppThemeFromLabel(String label) {
|
||||
return (label == darkThemeKey) ? AppTheme.Dark : AppTheme.Light;
|
||||
}
|
||||
|
||||
static String getTranslatedLabel(BuildContext context, String labelKey) {
|
||||
return context.read<LanguageJsonCubit>().getTranslatedLabels(labelKey);
|
||||
}
|
||||
|
||||
static Future<void> loginRequired(BuildContext context) {
|
||||
showSnackBar(UiUtils.getTranslatedLabel(context, 'loginReqMsg'), context);
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
return Navigator.of(context).pushNamed(Routes.login, arguments: {"isFromApp": true}); //pass isFromApp to get back to specified screen
|
||||
});
|
||||
return Future(() => null);
|
||||
}
|
||||
|
||||
static Widget showCircularProgress(bool isProgress, Color color) {
|
||||
if (isProgress) {
|
||||
return Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(color)));
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
showUploadImageBottomsheet({required BuildContext context, required VoidCallback? onCamera, required VoidCallback? onGallery}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(side: BorderSide(color: Colors.transparent), borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10))),
|
||||
builder: (BuildContext buildContext) {
|
||||
return SafeArea(
|
||||
child: Wrap(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: SvgPictureWidget(assetName: 'gallaryIcon', assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), BlendMode.srcIn)),
|
||||
title: CustomTextLabel(text: 'photoLibLbl', textStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer, fontSize: 16, fontWeight: FontWeight.w400)),
|
||||
onTap: onGallery),
|
||||
ListTile(
|
||||
leading: SvgPictureWidget(assetName: 'cameraIcon', assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), BlendMode.srcIn)),
|
||||
title: CustomTextLabel(text: 'cameraLbl', textStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer, fontSize: 16, fontWeight: FontWeight.w400)),
|
||||
onTap: onCamera),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
static String? convertToAgo(BuildContext context, DateTime input, int from) {
|
||||
//from - 0 : NewsItem,details, bookmarks & sectionStyle5 , 1 : Comments, 2: Notifications, 3: SectionStyle6
|
||||
Duration diff = DateTime.now().difference(input);
|
||||
if (Intl.defaultLocale != null || Intl.defaultLocale!.isEmpty) Intl.defaultLocale = 'en';
|
||||
initializeDateFormatting(); //locale according to location
|
||||
final langCode = Hive.box(settingsBoxKey).get(currentLanguageCodeKey);
|
||||
bool isNegative = diff.isNegative;
|
||||
if (diff.inDays >= 365 && from == 1) {
|
||||
double years = calculateYearsDifference(input, DateTime.now());
|
||||
return "${years.toStringAsFixed(0)} ${getTranslatedLabel(context, 'years')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else {
|
||||
if (diff.inDays >= 1 || (isNegative && diff.inDays < 1)) {
|
||||
if (from == 0) {
|
||||
var newFormat = DateFormat("MMM dd, yyyy", langCode);
|
||||
final newsDate1 = newFormat.format(input);
|
||||
return newsDate1;
|
||||
} else if (from == 1) {
|
||||
int months = calculateMonthsDifference(input, DateTime.now());
|
||||
if ((months < 12 && diff.inDays >= 30) && !isNegative) {
|
||||
return "${months.toStringAsFixed(0)} ${getTranslatedLabel(context, 'months')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else if ((diff.inHours >= 1 && diff.inHours < 24)) {
|
||||
return "${getTranslatedLabel(context, 'about')} ${diff.inHours} ${getTranslatedLabel(context, 'hours')} ${input.minute} ${getTranslatedLabel(context, 'minutes')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else if ((isNegative && diff.inMinutes < 1)) {
|
||||
return "${getTranslatedLabel(context, 'about')} ${input.minute} ${getTranslatedLabel(context, 'minutes')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else {
|
||||
return "${diff.inDays} ${getTranslatedLabel(context, 'days')} ${getTranslatedLabel(context, 'ago')}";
|
||||
}
|
||||
} else if (from == 2) {
|
||||
var newFormat = DateFormat("dd MMMM yyyy", langCode);
|
||||
final newsDate1 = newFormat.format(input);
|
||||
return newsDate1;
|
||||
} else if (from == 3) {
|
||||
var newFormat = DateFormat("MMMM dd, yyyy", langCode);
|
||||
final newNewsDate = newFormat.format(input);
|
||||
return newNewsDate;
|
||||
}
|
||||
} else if (diff.inHours >= 1 || (isNegative && diff.inMinutes < 1)) {
|
||||
if (input.minute == 00) {
|
||||
return "${diff.inHours} ${getTranslatedLabel(context, 'hours')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else {
|
||||
if (from == 2) {
|
||||
return "${getTranslatedLabel(context, 'about')} ${diff.inHours} ${getTranslatedLabel(context, 'hours')} ${input.minute} ${getTranslatedLabel(context, 'minutes')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else {
|
||||
return "${diff.inHours} ${getTranslatedLabel(context, 'hours')} ${input.minute} ${getTranslatedLabel(context, 'minutes')} ${getTranslatedLabel(context, 'ago')}";
|
||||
}
|
||||
}
|
||||
} else if (diff.inMinutes >= 1 || (isNegative && diff.inMinutes < 1)) {
|
||||
return "${diff.inMinutes} ${getTranslatedLabel(context, 'minutes')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else if (diff.inSeconds >= 1) {
|
||||
return "${diff.inSeconds} ${getTranslatedLabel(context, 'seconds')} ${getTranslatedLabel(context, 'ago')}";
|
||||
} else {
|
||||
return getTranslatedLabel(context, 'justNow');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static int calculateMonthsDifference(DateTime startDate, DateTime endDate) {
|
||||
int yearsDifference = endDate.year - startDate.year;
|
||||
int monthsDifference = endDate.month - startDate.month;
|
||||
|
||||
return yearsDifference * 12 + monthsDifference;
|
||||
}
|
||||
|
||||
static double calculateYearsDifference(DateTime startDate, DateTime endDate) {
|
||||
int monthsDifference = calculateMonthsDifference(startDate, endDate);
|
||||
return monthsDifference / 12;
|
||||
}
|
||||
|
||||
/// Pluggable date picker theme builder for consistent styling across the app
|
||||
/// Usage: Theme(data: Theme.of(context).copyWith(datePickerTheme: UiUtils.buildDatePickerTheme(context)))
|
||||
static DatePickerThemeData buildDatePickerTheme(BuildContext context) {
|
||||
final colorScheme = getColorScheme(context);
|
||||
|
||||
return DatePickerThemeData(
|
||||
// Base styling
|
||||
dayStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
|
||||
// Header configuration
|
||||
headerBackgroundColor: colorScheme.primary,
|
||||
headerForegroundColor: colorScheme.onPrimary,
|
||||
|
||||
// Today/Current date styling - ensures proper visibility
|
||||
todayForegroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.onPrimary; // White text when selected
|
||||
}
|
||||
return colorScheme.primary; // Primary color when not selected - fixes white color issue
|
||||
}),
|
||||
todayBackgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.primary; // Primary background when selected
|
||||
}
|
||||
return Colors.transparent; // No background when not selected
|
||||
}),
|
||||
|
||||
// Regular day styling
|
||||
dayForegroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.onPrimary; // White text on selected days
|
||||
}
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return colorScheme.onSurface.withOpacity(0.38); // Disabled state
|
||||
}
|
||||
return colorScheme.onSurface; // Normal text color
|
||||
}),
|
||||
dayBackgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.primary; // Primary background for selected
|
||||
}
|
||||
return Colors.transparent; // Transparent for unselected
|
||||
}),
|
||||
|
||||
// Year picker styling
|
||||
yearForegroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.onPrimary;
|
||||
}
|
||||
return colorScheme.onSurface;
|
||||
}),
|
||||
yearBackgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return colorScheme.primary;
|
||||
}
|
||||
return Colors.transparent;
|
||||
}),
|
||||
|
||||
// Weekday header styling
|
||||
weekdayStyle: TextStyle(color: colorScheme.onSurface.withOpacity(0.6), fontWeight: FontWeight.w500, fontSize: 12),
|
||||
);
|
||||
}
|
||||
|
||||
/// Pluggable themed date picker wrapper that applies consistent styling
|
||||
/// Fixes the currentDate white color issue across the entire app
|
||||
/// Usage: UiUtils.showThemedDatePicker(context: context, ...)
|
||||
static Future<DateTime?> showThemedDatePicker({
|
||||
required BuildContext context,
|
||||
required DateTime initialDate,
|
||||
required DateTime firstDate,
|
||||
required DateTime lastDate,
|
||||
DateTime? currentDate,
|
||||
String? helpText,
|
||||
String? cancelText,
|
||||
String? confirmText,
|
||||
Locale? locale,
|
||||
bool useRootNavigator = true,
|
||||
}) async {
|
||||
return await showDialog<DateTime>(
|
||||
context: context,
|
||||
useRootNavigator: useRootNavigator,
|
||||
builder: (BuildContext context) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
datePickerTheme: buildDatePickerTheme(context),
|
||||
),
|
||||
child: DatePickerDialog(
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
currentDate: currentDate,
|
||||
helpText: helpText,
|
||||
cancelText: cancelText,
|
||||
confirmText: confirmText,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static setUIOverlayStyle({required AppTheme appTheme}) {
|
||||
appTheme == AppTheme.Light
|
||||
? SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarColor: backgroundColor.withOpacity(0.8), statusBarBrightness: Brightness.light, statusBarIconBrightness: Brightness.dark))
|
||||
: SystemChrome.setSystemUIOverlayStyle(
|
||||
SystemUiOverlayStyle(statusBarColor: darkSecondaryColor.withOpacity(0.8), statusBarBrightness: Brightness.dark, statusBarIconBrightness: Brightness.light));
|
||||
}
|
||||
|
||||
static userLogOut({required BuildContext contxt}) {
|
||||
for (int i = 0; i < AuthProviders.values.length; i++) {
|
||||
if (AuthProviders.values[i].name == contxt.read<AuthCubit>().getType()) {
|
||||
contxt.read<BookmarkCubit>().resetState();
|
||||
contxt.read<LikeAndDisLikeCubit>().resetState();
|
||||
contxt.read<AuthCubit>().signOut(AuthProviders.values[i]).then((value) {
|
||||
Navigator.of(contxt).pushNamedAndRemoveUntil(Routes.login, (route) => false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//widget for User Profile Picture in Comments
|
||||
static Widget setFixedSizeboxForProfilePicture({required Widget childWidget}) {
|
||||
return SizedBox(height: 35, width: 35, child: childWidget);
|
||||
}
|
||||
|
||||
static Future<bool> isValidLocale(String locale) async {
|
||||
try {
|
||||
await initializeDateFormatting(locale, null);
|
||||
DateFormat('EEEE', locale).format(DateTime.now()); // test formatting
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> setValidDefaultLocale(String? locale) async {
|
||||
if (locale == null || locale.isEmpty || !(await isValidLocale(locale))) {
|
||||
Intl.defaultLocale = 'en';
|
||||
} else {
|
||||
Intl.defaultLocale = locale;
|
||||
}
|
||||
Hive.box(settingsBoxKey).put(currentLanguageCodeKey, Intl.defaultLocale);
|
||||
}
|
||||
|
||||
static Future<void> checkIfValidLocale({required String langCode}) async {
|
||||
await setValidDefaultLocale(langCode); //pass langCode here
|
||||
}
|
||||
|
||||
//Add & Edit News Screen
|
||||
//roundedRectangle dashed border widget
|
||||
static Widget dottedRRectBorder({required Widget childWidget}) {
|
||||
return DottedBorder(options: RoundedRectDottedBorderOptions(radius: const Radius.circular(10), dashPattern: const [6, 3]), child: ClipRRect(child: Center(child: childWidget)));
|
||||
}
|
||||
|
||||
static Widget dropdownArrow({required BuildContext context}) {
|
||||
return Align(alignment: Alignment.centerRight, child: Icon(Icons.keyboard_arrow_down_outlined, color: getColorScheme(context).primaryContainer));
|
||||
}
|
||||
|
||||
static Widget setRowWithContainer({required BuildContext context, required Widget firstChild, required bool isContentTypeUpload}) {
|
||||
return Container(
|
||||
width: double.maxFinite,
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 15),
|
||||
decoration: BoxDecoration(color: getColorScheme(context).surface, borderRadius: BorderRadius.circular(10.0)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
firstChild,
|
||||
(isContentTypeUpload)
|
||||
? Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 20.0),
|
||||
child: Align(alignment: Alignment.centerRight, child: Icon(Icons.file_upload_outlined, color: getColorScheme(context).primaryContainer)))
|
||||
: dropdownArrow(context: context)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget setBottomsheetContainer({required String listItem, required String compareTo, required BuildContext context, required String entryId}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0), color: (compareTo != "" && compareTo == entryId) ? Theme.of(context).primaryColor : getColorScheme(context).primaryContainer.withOpacity(0.1)),
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
alignment: Alignment.center,
|
||||
child: CustomTextLabel(
|
||||
text: listItem,
|
||||
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: (compareTo == entryId) ? getColorScheme(context).secondary : getColorScheme(context).primaryContainer)));
|
||||
}
|
||||
|
||||
static Widget setTopPaddingParent({required Widget childWidget}) {
|
||||
return Padding(padding: const EdgeInsets.only(top: 10.0), child: childWidget);
|
||||
}
|
||||
|
||||
//Home Screen - featured sections
|
||||
static Widget setPlayButton({required BuildContext context, double heightVal = 40}) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
height: heightVal,
|
||||
width: heightVal,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: Theme.of(context).primaryColor),
|
||||
child: const Icon(Icons.play_arrow_sharp, size: 25, color: secondaryColor));
|
||||
}
|
||||
|
||||
//Native Ads
|
||||
static BannerAd createBannerAd({required BuildContext context}) {
|
||||
return BannerAd(
|
||||
adUnitId: context.read<AppConfigurationCubit>().bannerId()!,
|
||||
request: const AdRequest(),
|
||||
size: AdSize.mediumRectangle,
|
||||
listener: BannerAdListener(
|
||||
onAdLoaded: (_) => debugPrint("native ad is Loaded !!!"),
|
||||
onAdFailedToLoad: (ad, err) {
|
||||
ad.dispose();
|
||||
},
|
||||
onAdOpened: (Ad ad) => debugPrint('Native ad opened.'),
|
||||
// Called when an ad opens an overlay that covers the screen.
|
||||
onAdClosed: (Ad ad) => debugPrint('Native ad closed.'),
|
||||
// Called when an ad removes an overlay that covers the screen.
|
||||
onAdImpression: (Ad ad) => debugPrint('Native ad impression.')));
|
||||
}
|
||||
|
||||
static Widget bannerAdsShow({required BuildContext context}) {
|
||||
return AdWidget(key: UniqueKey(), ad: createBannerAd(context: context)..load());
|
||||
}
|
||||
|
||||
//Interstitial Ads
|
||||
static showInterstitialAds({required BuildContext context}) {
|
||||
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") {
|
||||
if (context.read<AppConfigurationCubit>().checkAdsType() == "google") {
|
||||
showGoogleInterstitialAd(context);
|
||||
} else {
|
||||
showUnityInterstitialAds(context.read<AppConfigurationCubit>().interstitialId()!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shareNews(
|
||||
{required BuildContext context, required String title, required String slug, required bool isBreakingNews, required bool isNews, required bool isVideo, required String videoId}) {
|
||||
String contentType = "";
|
||||
if (isVideo) contentType = "video-news";
|
||||
if (isNews) contentType = "news";
|
||||
if (isBreakingNews) contentType = "breaking-news";
|
||||
|
||||
String shareLink = "$shareNavigationWebUrl/${context.read<AppLocalizationCubit>().state.languageCode}/$contentType/$slug?language_id=${context.read<AppLocalizationCubit>().state.id}&share=true";
|
||||
|
||||
String str = "$title\n\n$appName\n\n${context.read<AppConfigurationCubit>().getShareAppText()}\n\n${context.read<AppConfigurationCubit>().getAndroidAppLink()}\n";
|
||||
|
||||
bool isIOSAppLive = context.read<AppConfigurationCubit>().getiOSAppLink()?.trim().isNotEmpty ?? false;
|
||||
if (isIOSAppLive) str += "\n\n${context.read<AppConfigurationCubit>().getiOSAppLink()}";
|
||||
str = shareLink + "\n\n" + str;
|
||||
Share.share(str, subject: appName, sharePositionOrigin: Rect.fromLTWH(0, 0, MediaQuery.of(context).size.width, MediaQuery.of(context).size.height / 2));
|
||||
}
|
||||
|
||||
//calculate time in Minutes to Read News Article
|
||||
static int calculateReadingTime(String text) {
|
||||
const wordsPerMinute = 200;
|
||||
final wordCount = text.trim().split(' ').length;
|
||||
final readTime = (wordCount / wordsPerMinute).ceil();
|
||||
return readTime;
|
||||
}
|
||||
|
||||
static Widget applyBoxShadow({required BuildContext context, Widget? child}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).canvasColor,
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.1), offset: Offset(0, 3), blurRadius: 6),
|
||||
],
|
||||
),
|
||||
child: child);
|
||||
}
|
||||
|
||||
static String formatDate(String date) {
|
||||
DateTime dateTime = DateTime.parse(date);
|
||||
final DateFormat formatter = DateFormat("MMMM d,yyyy");
|
||||
return formatter.format(dateTime);
|
||||
}
|
||||
|
||||
static gotoStores(BuildContext context) async {
|
||||
String iosLink = context.read<AppConfigurationCubit>().getiOSAppLink() ?? "";
|
||||
String androidLink = context.read<AppConfigurationCubit>().getAndroidAppLink() ?? "";
|
||||
|
||||
if (await canLaunchUrl(Uri.parse((Platform.isIOS) ? iosLink : androidLink))) {
|
||||
await launchUrl(Uri.parse((Platform.isIOS) ? iosLink : androidLink), mode: LaunchMode.externalApplication);
|
||||
}
|
||||
if (Navigator.of(context).canPop()) Navigator.of(context).pop(false);
|
||||
}
|
||||
|
||||
static String decryptKey({required geminiKey}) {
|
||||
// Decode Base64 to bytes
|
||||
Uint8List bytes = base64.decode(geminiKey);
|
||||
|
||||
// Convert bytes to String (if it's text)
|
||||
String decodedString = utf8.decode(bytes);
|
||||
return decodedString;
|
||||
}
|
||||
}
|
||||
|
||||
Widget nativeAdsShow({required BuildContext context, required int index}) {
|
||||
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
|
||||
context.read<AppConfigurationCubit>().checkAdsType() != null &&
|
||||
(context.read<AppConfigurationCubit>().getIOSAdsType() != "unity" || context.read<AppConfigurationCubit>().getAdsType() != "unity") &&
|
||||
index != 0 &&
|
||||
index % nativeAdsIndex == 0) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 15.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(7.0),
|
||||
height: 300,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(10.0)),
|
||||
child: context.read<AppConfigurationCubit>().checkAdsType() == "google" && (context.read<AppConfigurationCubit>().bannerId() != "")
|
||||
? UiUtils.bannerAdsShow(context: context)
|
||||
: SizedBox.shrink()));
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
119
news-app/lib/utils/validators.dart
Normal file
119
news-app/lib/utils/validators.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
|
||||
class Validators {
|
||||
//name validation check
|
||||
static String? nameValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'nameRequired');
|
||||
}
|
||||
if (value.length <= 1) {
|
||||
return UiUtils.getTranslatedLabel(context, 'nameLength');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//email validation check
|
||||
static String? emailValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'emailRequired');
|
||||
} else if (!RegExp(r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)"
|
||||
r"*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+"
|
||||
r"[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
|
||||
.hasMatch(value)) {
|
||||
return UiUtils.getTranslatedLabel(context, 'emailValid');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//password validation check
|
||||
static String? passValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'pwdRequired');
|
||||
} else if (value.length <= 5) {
|
||||
return UiUtils.getTranslatedLabel(context, 'pwdLength');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String? mobValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'mblRequired');
|
||||
}
|
||||
if (value.length < 9 || value.length > 16) {
|
||||
return UiUtils.getTranslatedLabel(context, 'mblValid');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? titleValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'newsTitleReqLbl');
|
||||
} else if (value.length < 2) {
|
||||
return UiUtils.getTranslatedLabel(context, 'plzAddValidTitleLbl');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? youtubeUrlValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'urlReqLbl');
|
||||
} else {
|
||||
bool isValidURL = RegExp(r'^(((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?)').hasMatch(value);
|
||||
if (!isValidURL) return UiUtils.getTranslatedLabel(context, 'plzValidUrlLbl');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? urlValidation(String value, BuildContext context) {
|
||||
bool? test;
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'urlReqLbl');
|
||||
} else {
|
||||
validUrl(value).then((result) {
|
||||
test = result;
|
||||
if (test!) {
|
||||
return UiUtils.getTranslatedLabel(context, 'plzValidUrlLbl');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<bool> validUrl(String value) async {
|
||||
await Dio().head(value).then((value) {
|
||||
return (value.statusCode == 200) ? false : true;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//name validation check
|
||||
static String? emptyFieldValidation(String value, String hintText, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
switch (hintText) {
|
||||
case 'metaTitleLbl':
|
||||
return UiUtils.getTranslatedLabel(context, 'metaTitleRequired');
|
||||
case 'metaDescriptionLbl':
|
||||
return UiUtils.getTranslatedLabel(context, 'metaDescriptionRequired');
|
||||
case 'metaKeywordLbl':
|
||||
return UiUtils.getTranslatedLabel(context, 'metaKeywordRequired');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? slugValidation(String value, BuildContext context) {
|
||||
if (value.isEmpty) {
|
||||
return UiUtils.getTranslatedLabel(context, 'slugRequired');
|
||||
} else if (!RegExp("^[a-z0-9]+(?:-[a-z0-9]+)*").hasMatch(value)) {
|
||||
return UiUtils.getTranslatedLabel(context, 'slugValid');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user