diff --git a/android/app/build.gradle b/android/app/build.gradle index be123a0e..8263bcb5 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -20,12 +20,12 @@ if (project.hasProperty('google-services.json')) { def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = "25" + flutterVersionCode = "26" } def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = "1.2.17" + flutterVersionName = "1.2.18" } def keystorePropertiesFile = rootProject.file("key.properties") diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index fab5107c..66843ffb 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -513,7 +513,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.15; + MARKETING_VERSION = 1.2.16; PRODUCT_BUNDLE_IDENTIFIER = ae.gov.fcsc.frontend.ios; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -707,7 +707,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.15; + MARKETING_VERSION = 1.2.16; PRODUCT_BUNDLE_IDENTIFIER = ae.gov.fcsc.frontend.ios; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -737,7 +737,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.15; + MARKETING_VERSION = 1.2.16; PRODUCT_BUNDLE_IDENTIFIER = ae.gov.fcsc.frontend.ios; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/lib/config/api_config.dart b/lib/config/api_config.dart index 997fb132..c32efc27 100755 --- a/lib/config/api_config.dart +++ b/lib/config/api_config.dart @@ -1,5 +1,5 @@ // api_config.dart // const String apiUrl = 'http://127.0.0.1:8090'; // const String apiUrl = 'https://pbdev.venbait.in/'; -// const String apiUrl = 'https://pb.venbait.in/'; -const String apiUrl = 'https://pocket.fcsc.gov.ae/'; +const String apiUrl = 'https://pb.venbait.in/'; +// const String apiUrl = 'https://pocket.fcsc.gov.ae/'; diff --git a/lib/config/sessionCheckScreen.dart b/lib/config/sessionCheckScreen.dart index 50431f8d..02f5fa0a 100755 --- a/lib/config/sessionCheckScreen.dart +++ b/lib/config/sessionCheckScreen.dart @@ -11,7 +11,7 @@ class AuthRepository { Future fetchLocallyStored() async { return await _secureStorage.read(key: "FlutterSecureStorage.session"); } -} +} class SessionCheckScreen extends StatefulWidget { const SessionCheckScreen({Key? key}) : super(key: key); @@ -25,6 +25,14 @@ class _SessionCheckScreenState extends State { final String playStoreUrl = "https://play.google.com/store/apps/details?id=ae.gov.fcsc.stats"; bool _isLoading = true; + bool _isLoginPath(String path) { + return path == '/login' || path.startsWith('/login?'); + } + + bool _isForgotPasswordPath(String path) { + return path.startsWith('/forgot-password/'); + } + @override void initState() { super.initState(); @@ -43,20 +51,34 @@ class _SessionCheckScreenState extends State { .uri; final intendedPath = uri.queryParameters['intendedPath']; + final normalizedIntendedPath = + (intendedPath != null && intendedPath.isNotEmpty) + ? Uri.decodeComponent(intendedPath) + : null; if (session != null && session.freshness != SessionFreshness.expired) { - if (intendedPath != null && intendedPath.isNotEmpty) { - context.go(intendedPath); + if (normalizedIntendedPath != null && normalizedIntendedPath.isNotEmpty) { + // Deep link to /login should open home when session is already valid. + if (_isLoginPath(normalizedIntendedPath)) { + context.go('/myhomepage'); + } else { + context.go(normalizedIntendedPath); + } } else { context.go('/myhomepage'); } } else { - if (intendedPath != null && intendedPath.isNotEmpty) { - context.go('/login?intendedPath=${Uri.encodeComponent(intendedPath)}'); + if (normalizedIntendedPath != null && normalizedIntendedPath.isNotEmpty) { + // Forgot password links are public and should open directly. + if (_isForgotPasswordPath(normalizedIntendedPath)) { + context.go(normalizedIntendedPath); + } else { + context.go('/login?intendedPath=${Uri.encodeComponent(normalizedIntendedPath)}'); + } } else { context.go('/login'); } diff --git a/lib/config/theme/themeDialog.dart b/lib/config/theme/themeDialog.dart index f438fd0f..2f5573d4 100755 --- a/lib/config/theme/themeDialog.dart +++ b/lib/config/theme/themeDialog.dart @@ -50,34 +50,95 @@ class _ThemeSelectorDialogState extends State { @override Widget build(BuildContext context) { + final isDarkTheme = Theme.of(context).brightness == Brightness.dark; + const accentColor = Color(0xFFB68A34); + return AlertDialog( + backgroundColor: isDarkTheme ? Colors.black : Colors.white, title: Text( _tr(en: 'Choose Theme', ar: 'اختر المظهر'), + style: TextStyle( + color: isDarkTheme ? Colors.white : Colors.black, + ), ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: AppTheme.values.map((theme) { - return RadioListTile( - title: Text(_themeLabel(theme)), - value: theme, - groupValue: _selectedTheme, - onChanged: (value) async { - final prefs = await SharedPreferences.getInstance(); - await prefs.setString( - 'ThemeType', value.toString().split('.').last); + content: SizedBox( + width: double.maxFinite, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: AppTheme.values.map((theme) { + return RadioListTile( + contentPadding: EdgeInsets.zero, + controlAffinity: ListTileControlAffinity.leading, + activeColor: accentColor, + fillColor: WidgetStateProperty.all(accentColor), + title: Text( + _themeLabel(theme), + style: TextStyle( + color: isDarkTheme ? Colors.white : Colors.black, + ), + ), + value: theme, + groupValue: _selectedTheme, + onChanged: (value) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString( + 'ThemeType', value.toString().split('.').last); - setState(() => _selectedTheme = value!); - }); - }).toList(), + setState(() => _selectedTheme = value!); + }); + }).toList(), + ), ), actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(null), - child: Text(_tr(en: 'Cancel', ar: 'إلغاء')), - ), - ElevatedButton( - onPressed: () => Navigator.of(context).pop(_selectedTheme), - child: Text(_tr(en: 'OK', ar: 'حسناً')), + Row( + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.of(context).pop(null), + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + side: const BorderSide( + color: accentColor, + width: 1, + ), + ), + child: Text( + _tr(en: 'Cancel', ar: 'إلغاء'), + style: const TextStyle( + color: accentColor, + fontSize: 16, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextButton( + onPressed: () => Navigator.of(context).pop(_selectedTheme), + style: TextButton.styleFrom( + backgroundColor: accentColor, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + ), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + _tr(en: 'OK', ar: 'حسناً'), + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + ), + ), + ), + ], ), ], ); diff --git a/lib/main.dart b/lib/main.dart index 6acd2e24..47131b3e 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -92,6 +92,7 @@ class _MainAppState extends ConsumerState { String? pendingDeepLink; Map? pendingDeepLinkData; StreamSubscription? _appLinkSubscription; + Timer? _oneLinkFallbackTimer; @override void initState() { @@ -104,6 +105,7 @@ class _MainAppState extends ConsumerState { @override void dispose() { _appLinkSubscription?.cancel(); + _oneLinkFallbackTimer?.cancel(); super.dispose(); } @@ -123,8 +125,15 @@ class _MainAppState extends ConsumerState { if (uri.scheme == 'fcscappstates') { final deepLinkValue = uri.queryParameters['deep_link_value']; if (deepLinkValue != null && deepLinkValue.isNotEmpty) { + _oneLinkFallbackTimer?.cancel(); final path = deepLinkValue.startsWith('/') ? deepLinkValue : '/$deepLinkValue'; router.go('/?intendedPath=${Uri.encodeComponent(path)}'); + } else { + // Wait briefly for AppsFlyer SDK callback, then fallback to app root. + _oneLinkFallbackTimer?.cancel(); + _oneLinkFallbackTimer = Timer(const Duration(milliseconds: 1500), () { + if (mounted) router.go('/'); + }); } return; } @@ -133,8 +142,17 @@ class _MainAppState extends ConsumerState { if (uri.scheme == 'https' && uri.host == 'fcscstats.onelink.me') { final deepLinkValue = uri.queryParameters['deep_link_value']; if (deepLinkValue != null && deepLinkValue.isNotEmpty) { + _oneLinkFallbackTimer?.cancel(); final path = deepLinkValue.startsWith('/') ? deepLinkValue : '/$deepLinkValue'; router.go('/?intendedPath=${Uri.encodeComponent(path)}'); + } else { + // Some OneLink opens arrive without query params; AppsFlyer SDK usually + // delivers deep_link_value shortly after. Delay root fallback to avoid + // overriding the real deep-link route. + _oneLinkFallbackTimer?.cancel(); + _oneLinkFallbackTimer = Timer(const Duration(milliseconds: 1500), () { + if (mounted) router.go('/'); + }); } return; } @@ -204,13 +222,37 @@ class _MainAppState extends ConsumerState { } void _handleDeepLink(String? deepLinkValue, Map data) { - if (deepLinkValue == null || deepLinkValue.isEmpty) return; + _oneLinkFallbackTimer?.cancel(); + + String? resolved = deepLinkValue; + + // AppsFlyer may not always populate deepLinkValue for complex links. + // Fallback to clickEvent payload keys. + resolved ??= data['deep_link_value']?.toString(); + resolved ??= data['af_dp']?.toString(); + resolved = resolved?.trim(); + + if (resolved == null || resolved.isEmpty) return; + + // If AppsFlyer gives a full URL, preserve path+query only. + if (resolved.startsWith('http://') || resolved.startsWith('https://')) { + final parsed = Uri.tryParse(resolved); + if (parsed != null) { + resolved = parsed.path + (parsed.hasQuery ? '?${parsed.query}' : ''); + } + } + + // Decode once so encoded query paths like + // %2FchartScreen%2Fgdp%3FbgColor%3D... become route-friendly. + try { + resolved = Uri.decodeComponent(resolved); + } catch (_) {} // Use full path as intendedPath so shared links (e.g. /uaenumbers/123) open the right screen. // SessionCheckScreen: if logged in -> go to intendedPath; if not -> login then redirect. - final String intendedPath = deepLinkValue.startsWith('/') - ? deepLinkValue - : '/$deepLinkValue'; + final String intendedPath = resolved!.startsWith('/') + ? resolved + : '/$resolved'; context.go('/?intendedPath=${Uri.encodeComponent(intendedPath)}'); } diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index 73b59557..37821a2f 100755 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -2370,7 +2370,9 @@ class _ChartScreen1State extends ConsumerState { // OneLink template V6mR; configure in AppsFlyer dashboard: app package/bundle ID + store fallback URLs const String baseAppLink = 'https://fcscstats.onelink.me/V6mR/t40b32al'; final String path = currentRoute.startsWith('/') ? currentRoute : '/$currentRoute'; - final String shareLink = path == '/' ? baseAppLink : '$baseAppLink$path'; + final String shareLink = path == '/' + ? baseAppLink + : '$baseAppLink?deep_link_value=${Uri.encodeComponent(path)}'; final String shareText = 'Check this out!\n\n$shareLink'; print('Generated Share Link: $shareLink'); diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index 6fb181c2..c8070771 100755 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -229,29 +229,29 @@ class LoginRoute extends HookConsumerWidget with WidgetsBindingObserver{ backgroundColor: isDark ? const Color(0xFF1F1F1F) : Colors.white, title: showErrorTitle ? Column( - mainAxisSize: MainAxisSize.min, - children: [ - const CircleAvatar( - radius: 18, - backgroundColor: Color(0xFFFFE9E9), - child: Icon( - Icons.priority_high_rounded, - color: Color(0xFFD83731), - size: 22, - ), - ), - const SizedBox(height: 8), - Text( - context.translate('Error!', 'خطأ!'), - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: context.translate('Roboto', 'NotoKufi'), - fontWeight: FontWeight.w700, - color: isDark ? Colors.white : const Color(0xFF1A1A1A), - ), - ), - ], - ) + mainAxisSize: MainAxisSize.min, + children: [ + const CircleAvatar( + radius: 18, + backgroundColor: Color(0xFFFFE9E9), + child: Icon( + Icons.priority_high_rounded, + color: Color(0xFFD83731), + size: 22, + ), + ), + const SizedBox(height: 8), + Text( + context.translate('Error!', 'خطأ!'), + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: context.translate('Roboto', 'NotoKufi'), + fontWeight: FontWeight.w700, + color: isDark ? Colors.white : const Color(0xFF1A1A1A), + ), + ), + ], + ) : null, content: Text( message, @@ -674,11 +674,11 @@ class LoginRoute extends HookConsumerWidget with WidgetsBindingObserver{ } Future oAuthGoogleAndAppleLogin( - BuildContext context, - WidgetRef ref, - platform, { - bool linkToExistingAccount = false, - }) async { + BuildContext context, + WidgetRef ref, + platform, { + bool linkToExistingAccount = false, + }) async { print('platform $platform'); try { final authData; @@ -696,8 +696,9 @@ class LoginRoute extends HookConsumerWidget with WidgetsBindingObserver{ queryParameters: { ...url.queryParameters, 'acr_values': - 'urn:safelayer:tws:policies:authentication:level:low', + 'urn:safelayer:tws:policies:authentication:level:low', 'ui_locales': uiLocales, + // 'redirect_url': 'ae.gov.fcsc.stats://auth-callback', }, ); print(mobileUri); @@ -727,7 +728,7 @@ class LoginRoute extends HookConsumerWidget with WidgetsBindingObserver{ // Google must use external browser. Apple uses OAuthWebView on mobile per app flow. authData = await pb.collection('users').authWithOAuth2( platform, - (url) async { + (url) async { if (platform == 'apple' && !kIsWeb) { if (!context.mounted) return; await Navigator.of(context).push( @@ -2629,17 +2630,17 @@ class LoginRoute extends HookConsumerWidget with WidgetsBindingObserver{ dontHaveAnAccountRegisterBtn, // continueAsGuestBtn, 15.verticalSpace, - // orText, - // 15.verticalSpace, - // signInWithUAEPassBtn, - // 15.verticalSpace, - // orText, + orText, + 15.verticalSpace, + signInWithUAEPassBtn, + 15.verticalSpace, + orText, 15.verticalSpace, signInWithGoogleBtn, 15.verticalSpace, signInWithAppleBtn, - 15.verticalSpace, - signInWithUAEPassBtn, + // 15.verticalSpace, + // signInWithUAEPassBtn, 30.verticalSpace, // 120.verticalSpace, ], @@ -2791,29 +2792,48 @@ class OAuthWebView extends StatelessWidget { static const _resumePath = 'resume_authn'; Future _closeIfOAuthCompleted( - BuildContext context, - InAppWebViewController controller, - WebUri? url, - ) async { - if (useUaePassDeepLink || !context.mounted) return; + BuildContext context, + InAppWebViewController controller, + WebUri? url, + ) async { + if (!context.mounted) return; final s = (url?.toString() ?? '').toLowerCase(); - if (s.contains('/api/oauth2-redirect')) { - Navigator.of(context).pop(); + final authIsReady = PocketBaseService.authStore.isValid; + + if (s.contains('/api/oauth2-redirect') && s.contains('code=')) { + await Navigator.of(context).maybePop(); return; } - // PocketBase sometimes renders a static completion page. + if (s.contains('oauth2-redirect-success') || + s.contains('oauth2_redirect_success')) { + if (!useUaePassDeepLink && !authIsReady) return; + await Navigator.of(context).maybePop(); + return; + } + + // PocketBase/hosted success pages (old + new titles). final title = ((await controller.getTitle()) ?? '').toLowerCase(); - if (title.contains('auth completed')) { - Navigator.of(context).pop(); + if (title.contains('auth success') || + title.contains('auth completed') || + title.contains('authentication successful')) { + if (!useUaePassDeepLink && !authIsReady) return; + await Navigator.of(context).maybePop(); return; } final html = ((await controller.getHtml()) ?? '').toLowerCase(); - if (html.contains('auth completed') && - html.contains('you can close this window')) { - Navigator.of(context).pop(); + final legacySuccess = html.contains('auth completed') && + (html.contains('you can close this window') || + html.contains('close this window')); + final newSuccess = html.contains('authentication successful') && + (html.contains('proceed') || + html.contains('return to uae stats app')); + + if (legacySuccess || newSuccess) { + if (!useUaePassDeepLink && !authIsReady) return; + await Navigator.of(context).maybePop(); } } @@ -2828,13 +2848,29 @@ class OAuthWebView extends StatelessWidget { initialSettings: InAppWebViewSettings( javaScriptEnabled: true, domStorageEnabled: true, - useShouldOverrideUrlLoading: useUaePassDeepLink, + useShouldOverrideUrlLoading: true, ), - shouldOverrideUrlLoading: useUaePassDeepLink - ? (controller, navigationAction) async { + shouldOverrideUrlLoading: (controller, navigationAction) async { final uri = navigationAction.request.url; if (uri == null) return NavigationActionPolicy.ALLOW; final full = uri.toString(); + + // If callback deep link is about to open, forward to OS and close WebView. + if (full.toLowerCase().startsWith('$_appCallbackScheme://$_resumePath')) { + final callbackUri = Uri.tryParse(full); + if (callbackUri != null) { + await launchUrl(callbackUri, mode: LaunchMode.externalApplication); + } + if (context.mounted) { + await Navigator.of(context).maybePop(); + } + return NavigationActionPolicy.CANCEL; + } + + if (!useUaePassDeepLink) { + return NavigationActionPolicy.ALLOW; + } + // Match uaepass:// or uaepassstg:// (scheme can be missing on some platforms) if (!full.toLowerCase().startsWith('$_uaepassProdScheme://') && !full.toLowerCase().startsWith('$_uaepassStagingScheme://')) { @@ -2875,12 +2911,11 @@ class OAuthWebView extends StatelessWidget { mode: LaunchMode.externalApplication, ).then((launched) { if (launched && context.mounted) { - Navigator.of(context).pop(); + Navigator.of(context).maybePop(); } }); return NavigationActionPolicy.CANCEL; - } - : null, + }, onLoadStart: (controller, url) async => _closeIfOAuthCompleted(context, controller, url), onLoadStop: (controller, url) async => @@ -2891,7 +2926,7 @@ class OAuthWebView extends StatelessWidget { ); } } - + // abstract class _LocalAuthRepo { // static const _key = 'session'; diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/Detailedcompetitiveness.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/Detailedcompetitiveness.dart index 71226b02..5a1f4b39 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/Detailedcompetitiveness.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/Detailedcompetitiveness.dart @@ -185,8 +185,8 @@ class _ReportDetailPageState extends ConsumerState { ), child: BaseScaffold( showBackButton: true, - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( AppLocalizations.of(context)!.nav_competitiveness, style: TextStyle( @@ -199,7 +199,8 @@ class _ReportDetailPageState extends ConsumerState { ), body: isLoading || selectedReport.isEmpty ? const Center(child: CircularProgressIndicator()) - : Column( + : SingleChildScrollView( + child: Column( children: [ /// 🔹 Top Section With Padding Padding( @@ -255,7 +256,7 @@ class _ReportDetailPageState extends ConsumerState { /// 🔹 DESCRIPTION SizedBox( - height: 72, + height: 150, child: ScrollConfiguration( behavior: const ScrollBehavior().copyWith(scrollbars: false), child: SingleChildScrollView( @@ -367,7 +368,9 @@ class _ReportDetailPageState extends ConsumerState { ), /// 🔹 COUNTRY SECTION - Container( + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Container( decoration: BoxDecoration( color: isDarkTheme ? Color(0xFF222222) : Color(0xFFF5F5F5), borderRadius: BorderRadius.circular(12), @@ -391,8 +394,11 @@ class _ReportDetailPageState extends ConsumerState { ), ], )), + ), + const SizedBox(height: 24), ], ), + ), ) ) ); diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/competitiveness.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/competitiveness.dart index eaf07aab..2775b8dc 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/competitiveness.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/competitiveness.dart @@ -155,8 +155,8 @@ class _CompetitivenessState extends ConsumerState { ), ), child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( AppLocalizations.of(context)!.nav_competitiveness, style: TextStyle( diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/DetailedCountryProfilePage.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/DetailedCountryProfilePage.dart index 2c239afc..7882cbac 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/DetailedCountryProfilePage.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/DetailedCountryProfilePage.dart @@ -199,8 +199,8 @@ class _DetailedCountryProfilePageState ), ), child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( AppLocalizations.of(context)!.country_profile, style: TextStyle( @@ -1069,7 +1069,7 @@ class _CategoryPopupContentState extends ConsumerState { Text( tab['label'] as String, style: TextStyle( - fontSize: 13, + fontSize: 12, fontWeight: FontWeight.w400, color: isDarkTheme ? (isSelected @@ -1084,7 +1084,7 @@ class _CategoryPopupContentState extends ConsumerState { formatTradeValue(tab['total'] as double), style: TextStyle( fontWeight: FontWeight.w900, - fontSize: 22, + fontSize: 16, color: _getTradeColor(typeKey), ), ), diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/country_profile.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/country_profile.dart index a29d1127..35bfe6e0 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/country_profile.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/country_profile.dart @@ -159,8 +159,8 @@ class _ListCountriesRouteState extends ConsumerState { MediaQuery.of(context).platformBrightness == Brightness.dark); final body = BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( AppLocalizations.of(context)!.country_profile, style: TextStyle( @@ -186,7 +186,7 @@ class _ListCountriesRouteState extends ConsumerState { maxHeight: 50, ), elevation: WidgetStatePropertyAll(0), - backgroundColor: WidgetStatePropertyAll(isDarkTheme ? Color(0xFF111111) : Colors.white), + backgroundColor: WidgetStatePropertyAll(isDarkTheme ? Colors.black : Colors.white), controller: _searchController, hintText: AppLocalizations.of(context)!.searchCountry, hintStyle: WidgetStateProperty.all( diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/UAE_Numbers/uae_numbers.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/UAE_Numbers/uae_numbers.dart index 5da6cf47..b1315a2e 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/UAE_Numbers/uae_numbers.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/UAE_Numbers/uae_numbers.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; @@ -47,7 +48,7 @@ class UaeNumbers extends ConsumerWidget { context.go('/myhomepage'); // Show exit confirmation dialog }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF333333) : Color(0xFF414042), + backgroundColor: isDarkTheme ? Color(0xFF000000) : Color(0xFFFFFFFF), title: Text( context.translate( 'UAE Numbers', @@ -63,6 +64,7 @@ class UaeNumbers extends ConsumerWidget { ), body: uaenumberWidget(), ), + ); } } diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart index 98706d19..768f1d43 100755 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:pocketbase/pocketbase.dart'; @@ -60,7 +61,7 @@ class _MyHomePageState extends ConsumerState { 'NotoKufi', ), ), - ), + ), content: Padding( padding: const EdgeInsets.all(20.0), child: Text( @@ -156,7 +157,7 @@ class _MyHomePageState extends ConsumerState { (ref.watch(themeProvider) == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.dark); - return PopScope( +return PopScope( canPop: false, // Allow back navigation only if not login screen onPopInvokedWithResult: (didPop, result) { if (didPop) return; @@ -164,6 +165,7 @@ class _MyHomePageState extends ConsumerState { context, isDarkTheme); // Show exit confirmation dialog }, child: BaseScaffold( + backgroundColor: isDarkTheme ? Color(0xFF000000) : Color(0xFFFFFFFF), title: Center( child: SizedBox( height: myheight / 5, @@ -174,6 +176,7 @@ class _MyHomePageState extends ConsumerState { : LogoAssetPath.uaeStatLight)))), body: EconomyStatsWidget(), ), + ); } } @@ -1078,8 +1081,9 @@ class EconomyStatsState extends ConsumerState { return Center(child: Text("No data available")); } String firstMainTopic = data.first['main_topic'] ?? ''; - return Container( - color: isDarkTheme ? Color(0xFF000000) : Color(0xFFFFFFFF), + return SingleChildScrollView( + child: Container( + color: isDarkTheme ? Color(0xFF000000) : Color(0xFFFFFFFF), // decoration: isDarkTheme // ? BoxDecoration( // image: DecorationImage( @@ -1088,12 +1092,12 @@ class EconomyStatsState extends ConsumerState { // ), // ) // : null, - height: myheight, + width: double.infinity, // color: Colors.cyan, - child: Padding( - padding: EdgeInsets.only(left: 10, right: 10), - child: Column( - children: data.map((mainTopic) { + child: Padding( + padding: EdgeInsets.only(left: 10, right: 10), + child: Column( + children: data.map((mainTopic) { String colorPattern = mainTopic['color_pattern']; // Color backgroundColor = Color(int.parse(colorPattern)); @@ -1121,11 +1125,9 @@ class EconomyStatsState extends ConsumerState { final crossAxisCount = totalTiles > 0 ? (totalTiles / 2).ceil().clamp(1, 2) : 1; - return Expanded( - child: Container( + return Container( // color: Colors.cyan, - height: - myheight, // Set the background color for the entire Column + // Set the background color for the entire Column child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1205,9 +1207,9 @@ class EconomyStatsState extends ConsumerState { ), ], ), - ), - ); - }).toList(), + ); + }).toList(), + ), ), ), ); diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart b/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart index 8a438b4a..0b6ac016 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart @@ -519,8 +519,8 @@ class _BookMarkState extends ConsumerState { context.go('/myhomepage'); }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( AppLocalizations.of(context)!.bookmarks, style: TextStyle( @@ -532,7 +532,7 @@ class _BookMarkState extends ConsumerState { ), body: isLoading ? Container( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, // color: Color(0x98FFFCE5), // Semi-transparent background child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -591,7 +591,7 @@ class _BookMarkState extends ConsumerState { childWidget: Container( decoration: BoxDecoration( color: isDarkTheme - ? Color(0xFF111111) + ? Colors.black : Colors.white, // borderRadius: BorderRadius.all(Radius.circular(20)) ), @@ -768,7 +768,7 @@ class _BookMarkState extends ConsumerState { height: 100, padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: data['valueColor']), ), @@ -892,7 +892,7 @@ class _BookMarkState extends ConsumerState { height: 100, padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: styleColor), ), @@ -985,7 +985,7 @@ class TabBarHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, padding: const EdgeInsets.symmetric(vertical: 8), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -1081,7 +1081,7 @@ class _CustomExpandableTileState extends State { child: Container( decoration: BoxDecoration( color: widget.isDarkTheme - ? Color(0xFF111111) + ? Colors.black : Colors .white, // Ensuring the background outside the rounded container is white ), diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/contactUs.dart b/lib/presentation/routes/drawer_routes/Drawer Items/contactUs.dart index 980a6f53..3ffc6805 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/contactUs.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/contactUs.dart @@ -143,13 +143,13 @@ class _ContactState extends ConsumerState { context.go('/myhomepage'); }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF333333) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF333333) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, body: isLoading ? Container( // color: Color(0x98FFFCE5), // Semi-transparent background color: isDarkTheme - ? Color(0xFF333333) + ? Colors.black : Color(0x98FFFCE5), // Semi-transparent background child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -171,7 +171,7 @@ class _ContactState extends ConsumerState { : Container( // color: Colors.grey[200], // color: isDarkTheme ? Color(0xFF3B3C3F) : Colors.grey[200], - color: isDarkTheme ? Color(0xFF333333) : Colors.grey[200], + color: isDarkTheme ? Colors.black : Colors.grey[200], width: screenWidth, padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart index bb1309be..4d61298b 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -338,8 +338,8 @@ class _FeedbackFormState extends ConsumerState context.go('/myhomepage'); // Show exit confirmation dialog }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, // backgroundColor: isDarkTheme ? Color(0xFF333333) : Color(0xFF414042), // appbarColor: isDarkTheme ? Color(0xFF333333) : Color(0xFF414042), diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart index 5f85ad80..deda91d1 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart @@ -313,8 +313,8 @@ class _NotificationPageState extends ConsumerState { context.go('/myhomepage'); }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( context.translate( 'Notification $notificationCount', @@ -330,7 +330,7 @@ class _NotificationPageState extends ConsumerState { ? Container( // color: Color(0x98FFFCE5), // Semi-transparent background color: isDarkTheme - ? Color(0xFF111111) + ? Colors.black : Color(0x98FFFCE5), // Semi-transparent background child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -435,7 +435,7 @@ class TabBarHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, padding: const EdgeInsets.symmetric(vertical: 8), child: SingleChildScrollView( scrollDirection: Axis.horizontal, diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart index 7388fe92..c7de1156 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart @@ -211,8 +211,8 @@ class _NotificationDetailsState extends ConsumerState { context.go('/notification'); }, child: BaseScaffold( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, + appbarColor: isDarkTheme ? Colors.black : Colors.white, title: Text( context.translate( 'Notification', diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/user_guide/userguide.dart b/lib/presentation/routes/drawer_routes/Drawer Items/user_guide/userguide.dart index 2cc051cd..44f3761e 100755 --- a/lib/presentation/routes/drawer_routes/Drawer Items/user_guide/userguide.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/user_guide/userguide.dart @@ -99,15 +99,15 @@ class _UserguideState extends ConsumerState { }, child: BaseScaffold( showDivider: true, - bottomColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + bottomColor: isDarkTheme ? Colors.black : Colors.white, // dividerColor: isDarkTheme ? Color(0xFF111111) : Colors.grey[300], dividerColor: isDarkTheme ? Colors.grey : Colors.grey[300], - appbarColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + appbarColor: isDarkTheme ? Colors.black: Colors.white, title: Text(AppLocalizations.of(context)!.guide_title, style: TextStyle( color: isDarkTheme ? Colors.white : Color(0xFF414042))), body: Container( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), child: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( @@ -172,7 +172,7 @@ class _HoverContainerState extends ConsumerState { width: screenWidth * 0.30, margin: EdgeInsets.all(1), decoration: BoxDecoration( - color: isDarkTheme ? Color(0xFF111111) : Colors.white, + color: isDarkTheme ? Colors.black : Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: isHovered diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index d1cfa640..720c2993 100755 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1112,7 +1112,7 @@ class _BaseScaffoldState extends ConsumerState { ), ), drawer: Drawer( - backgroundColor: isDarkTheme ? Color(0xFF111111) : Colors.white, + backgroundColor: isDarkTheme ? Colors.black : Colors.white, child: Column(children: [ Expanded( child: ListView( @@ -1550,41 +1550,46 @@ class _BaseScaffoldState extends ConsumerState { ), ), ])), - bottomNavigationBar: Stack( - children: [ - // if (isDarkTheme) - // Positioned( - // bottom: 0, - // left: 0, - // right: 0, - // child: Container( - // height: kBottomNavigationBarHeight + 0, // Adjust as needed - // decoration: BoxDecoration( - // image: DecorationImage( - // image: AssetImage( - // 'assets/dark_bg.png'), // your dark mode image - // fit: BoxFit.cover, - // ), - // ), - // ), - // ), - BottomNavigationBar( + bottomNavigationBar: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.10), + blurRadius: 16, + spreadRadius: 1, + offset: const Offset(0, -2), + ), + ], + ), + child: ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + child: BottomNavigationBar( backgroundColor: isDarkTheme ? Color(0xFF000000) : Colors.white, currentIndex: _getSelectedIndex(currentRoute), onTap: (index) => _onItemTapped(context, index), type: BottomNavigationBarType.fixed, + elevation: 0, items: [ BottomNavigationBarItem( icon: Padding( - padding: const EdgeInsets.only( - bottom: 4), // Adjust this for spacing - child: Image.asset( - BottomBarAssetIconPath.home, - color: _getSelectedIndex(currentRoute) == 0 - ? (isDarkTheme ? Color(0xFFFFFFFF) : Colors.black) - : Color(0xFF989898), - width: 23.98, - height: 25, + padding: const EdgeInsets.only(bottom: 4), + child: SizedBox( + width: 24, + height: 24, + child: Image.asset( + BottomBarAssetIconPath.home, + fit: BoxFit.contain, + color: _getSelectedIndex(currentRoute) == 0 + ? Color(0xFFAA8E83) + : Color(0xFF989898), + ), ), ), // label: 'Home', @@ -1593,15 +1598,17 @@ class _BaseScaffoldState extends ConsumerState { BottomNavigationBarItem( // icon: Icon(Icons.map), icon: Padding( - padding: const EdgeInsets.only( - bottom: 4), // Adjust this for spacing - child: Image.asset( - BottomBarAssetIconPath.uaeMap, - color: _getSelectedIndex(currentRoute) == 1 - ? (isDarkTheme ? Color(0xFFFFFFFF) : Colors.black) - : Color(0xFF989898), - width: 23.98, - height: 25, + padding: const EdgeInsets.only(bottom: 4), + child: SizedBox( + width: 24, + height: 24, + child: Image.asset( + BottomBarAssetIconPath.uaeMap, + fit: BoxFit.contain, + color: _getSelectedIndex(currentRoute) == 1 + ? Color(0xFFAA8E83) + : Color(0xFF989898), + ), ), ), // label: 'UAE Numbers' @@ -1609,15 +1616,17 @@ class _BaseScaffoldState extends ConsumerState { ), BottomNavigationBarItem( icon: Padding( - padding: const EdgeInsets.only( - bottom: 4), // Adjust this for spacing - child: Image.asset( - BottomBarAssetIconPath.ranking, - color: _getSelectedIndex(currentRoute) == 2 - ? (isDarkTheme ? Color(0xFFFFFFFF) : Colors.black) - : Color(0xFF989898), - width: 23.98, - height: 25, + padding: const EdgeInsets.only(bottom: 4), + child: SizedBox( + width: 24, + height: 24, + child: Image.asset( + BottomBarAssetIconPath.ranking, + fit: BoxFit.contain, + color: _getSelectedIndex(currentRoute) == 2 + ? Color(0xFFAA8E83) + : Color(0xFF989898), + ), ), ), // label: 'Competitiveness', @@ -1625,15 +1634,17 @@ class _BaseScaffoldState extends ConsumerState { ), BottomNavigationBarItem( icon: Padding( - padding: const EdgeInsets.only( - bottom: 4), // Adjust this for spacing - child: Image.asset( - BottomBarAssetIconPath.globe, - color: _getSelectedIndex(currentRoute) == 3 - ? (isDarkTheme ? Color(0xFFFFFFFF) : Colors.black) - : Color(0xFF989898), - width: 23.98, - height: 25, + padding: const EdgeInsets.only(bottom: 4), + child: SizedBox( + width: 24, + height: 24, + child: Image.asset( + BottomBarAssetIconPath.globe, + fit: BoxFit.contain, + color: _getSelectedIndex(currentRoute) == 3 + ? Color(0xFFAA8E83) + : Color(0xFF989898), + ), ), ), // label: 'Country Profile', @@ -1641,7 +1652,7 @@ class _BaseScaffoldState extends ConsumerState { ), ], key: bottomNavKey, - selectedItemColor: isDarkTheme ? Color(0xFFFFFFFF) : Colors.black, + selectedItemColor: Color(0xFFAA8E83), unselectedItemColor: Color(0xFF989898), selectedLabelStyle: TextStyle( fontFamily: context.translate( @@ -1649,6 +1660,7 @@ class _BaseScaffoldState extends ConsumerState { 'NotoKufi', ), fontSize: 10, // Font size for unselected label + fontWeight: FontWeight.w700, ), unselectedLabelStyle: TextStyle( fontFamily: context.translate( @@ -1659,7 +1671,7 @@ class _BaseScaffoldState extends ConsumerState { ), showUnselectedLabels: true, ), - ], + ), ), // BottomNavigationBar( diff --git a/pubspec.yaml b/pubspec.yaml index 8adfcbba..f7aee7be 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,8 @@ name: uae_stat description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness." publish_to: "none" -#version: 1.2.17+25 -version: 1.2.15+23 +#version: 1.2.18+26 +version: 1.2.16+24 #version: 1.0.16+17 #version: 1.0.6+6