FCSC Feedback bug fix

This commit is contained in:
Surendiran 2026-04-25 15:03:07 +05:30
parent 4da8888796
commit 10f8ab2db0
22 changed files with 394 additions and 210 deletions

View File

@ -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")

View File

@ -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";

View File

@ -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/';

View File

@ -11,7 +11,7 @@ class AuthRepository {
Future<String?> 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<SessionCheckScreen> {
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<SessionCheckScreen> {
.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');
}

View File

@ -50,34 +50,95 @@ class _ThemeSelectorDialogState extends State<ThemeSelectorDialog> {
@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<AppTheme>(
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<AppTheme>(
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,
),
),
),
),
],
),
],
);

View File

@ -92,6 +92,7 @@ class _MainAppState extends ConsumerState<MainApp> {
String? pendingDeepLink;
Map<String, dynamic>? pendingDeepLinkData;
StreamSubscription<Uri>? _appLinkSubscription;
Timer? _oneLinkFallbackTimer;
@override
void initState() {
@ -104,6 +105,7 @@ class _MainAppState extends ConsumerState<MainApp> {
@override
void dispose() {
_appLinkSubscription?.cancel();
_oneLinkFallbackTimer?.cancel();
super.dispose();
}
@ -123,8 +125,15 @@ class _MainAppState extends ConsumerState<MainApp> {
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<MainApp> {
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<MainApp> {
}
void _handleDeepLink(String? deepLinkValue, Map<String, dynamic> 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)}');
}

View File

@ -2370,7 +2370,9 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
// 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');

View File

@ -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<void> 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<void> _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';

View File

@ -185,8 +185,8 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
),
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<ReportDetailPage> {
),
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<ReportDetailPage> {
/// 🔹 DESCRIPTION
SizedBox(
height: 72,
height: 150,
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: SingleChildScrollView(
@ -367,7 +368,9 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
),
/// 🔹 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<ReportDetailPage> {
),
],
)),
),
const SizedBox(height: 24),
],
),
),
)
)
);

View File

@ -155,8 +155,8 @@ class _CompetitivenessState extends ConsumerState<Competitiveness> {
),
),
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(

View File

@ -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<CategoryPopupContent> {
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<CategoryPopupContent> {
formatTradeValue(tab['total'] as double),
style: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 22,
fontSize: 16,
color: _getTradeColor(typeKey),
),
),

View File

@ -159,8 +159,8 @@ class _ListCountriesRouteState extends ConsumerState<ListCountriesRoute> {
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<ListCountriesRoute> {
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(

View File

@ -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(),
),
);
}
}

View File

@ -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<MyHomePage> {
'NotoKufi',
),
),
),
),
content: Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
@ -156,7 +157,7 @@ class _MyHomePageState extends ConsumerState<MyHomePage> {
(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<MyHomePage> {
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<MyHomePage> {
: LogoAssetPath.uaeStatLight)))),
body: EconomyStatsWidget(),
),
);
}
}
@ -1078,8 +1081,9 @@ class EconomyStatsState extends ConsumerState<EconomyStatsWidget> {
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<EconomyStatsWidget> {
// ),
// )
// : 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<EconomyStatsWidget> {
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<EconomyStatsWidget> {
),
],
),
),
);
}).toList(),
);
}).toList(),
),
),
),
);

View File

@ -519,8 +519,8 @@ class _BookMarkState extends ConsumerState<BookMark> {
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<BookMark> {
),
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<BookMark> {
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<BookMark> {
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<BookMark> {
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<CustomExpandableTile> {
child: Container(
decoration: BoxDecoration(
color: widget.isDarkTheme
? Color(0xFF111111)
? Colors.black
: Colors
.white, // Ensuring the background outside the rounded container is white
),

View File

@ -143,13 +143,13 @@ class _ContactState extends ConsumerState<Contact> {
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<Contact> {
: 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(

View File

@ -338,8 +338,8 @@ class _FeedbackFormState extends ConsumerState<FeedbackForm>
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),

View File

@ -313,8 +313,8 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
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<NotificationPage> {
? 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,

View File

@ -211,8 +211,8 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
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',

View File

@ -99,15 +99,15 @@ class _UserguideState extends ConsumerState<Userguide> {
},
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<HoverContainer> {
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

View File

@ -1112,7 +1112,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
),
),
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<BaseScaffold> {
),
),
])),
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<BaseScaffold> {
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<BaseScaffold> {
),
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<BaseScaffold> {
),
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<BaseScaffold> {
),
],
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<BaseScaffold> {
'NotoKufi',
),
fontSize: 10, // Font size for unselected label
fontWeight: FontWeight.w700,
),
unselectedLabelStyle: TextStyle(
fontFamily: context.translate(
@ -1659,7 +1671,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
),
showUnselectedLabels: true,
),
],
),
),
// BottomNavigationBar(

View File

@ -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