app optimize

This commit is contained in:
Surendiran 2025-08-28 14:20:10 +05:30
parent f992502261
commit 397e1b9f6c
61 changed files with 108 additions and 9224 deletions

View File

@ -2,4 +2,6 @@
// 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://pocket.fcsc.gov.ae';
// const String auth = 'kannan@sightspectrum.com';
// const String authpw = '1DgZ_we3k5UuCZiF9wziwECQzQo1YNRQ';

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// import 'package:app_links/app_links.dart';
import 'package:uae_stat/presentation/routes/auth_routes/login_route.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart';
class DeepLinkListener extends StatefulWidget {
const DeepLinkListener({super.key, required this.child});

View File

@ -1,36 +0,0 @@
import 'package:flutter/material.dart';
class LoadingOverlay {
static final ValueNotifier<bool> _isLoading = ValueNotifier<bool>(false);
static void show() {
_isLoading.value = true;
}
static void hide() {
_isLoading.value = false;
}
static Widget buildLoader() {
return ValueListenableBuilder<bool>(
valueListenable: _isLoading,
builder: (context, isLoading, child) {
return isLoading
? Stack(
children: [
ModalBarrier(
color: Colors.black.withOpacity(0.5), dismissible: false),
Center(
child: Image.asset(
"assets/FCSC-GIF.gif", // Make sure to add the GIF in assets
width: 100,
height: 100,
),
),
],
)
: const SizedBox.shrink();
},
);
}
}

View File

@ -1,297 +1,3 @@
// import 'package:external_repos/external_repos.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_localizations/flutter_localizations.dart';
// import 'package:go_router/go_router.dart';
// import 'package:riverpod_annotation/riverpod_annotation.dart';
// import 'package:uae_stat/domain/use_cases/auth_use_case.dart';
// import 'package:uae_stat/domain/use_cases/language.dart';
// import 'package:uae_stat/domain/use_cases/preferences_use_case.dart';
// import 'package:uae_stat/infrastructure/services/packages/go_router.dart';
// import 'package:uae_stat/presentation/components/logged_in_home_scaffold.dart';
// import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
// import 'package:uae_stat/presentation/components/my_drawer.dart';
// import 'package:uae_stat/presentation/routes/auth_routes/login_route.dart';
// import 'package:uae_stat/presentation/routes/auth_routes/register_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/competitiveness_flow/report_flow/list_reports_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/competitiveness_flow/report_flow/report_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/country_profile_flow/country_statistics_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/country_profile_flow/list_countries_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/list_uae_numbers_route.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/economy/_aircraft_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/economy/_gdp_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/economy/_hotels_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/economy/_inflation_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/economy/_trade_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/environment/_area_of_national_reserves_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/environment/_oil_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/environment/_water_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/environment/electricity_consumption_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/environment/electricity_production_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/social/_general_education_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/social/_higher_education_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/social/_hospitals_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/social/_labor_force_body.dart';
// import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/sub_routes/social/_population_body.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/feedback_route.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/manage_user_router.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/my_favorites_route.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/privacy_policy_route.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/profile/reset_pw_route.dart';
//
// import '../presentation/routes/drawer_routes/profile/profile_route.dart';
//
// part 'my_router.g.dart';
//
// class _MyRouter {
// const _MyRouter({
// required this.isLoggedIn,
// });
//
// final bool isLoggedIn;
//
// static FutureOr<String?> rootRedirect(
// BuildContext context,
// GoRouterState state,
// ) {
// final defaultLanguage = PreferencesUseCase.defaultPrefs.language.name;
// if (state.fullPath == '/') {
// return '/$defaultLanguage/home';
// }
// final currentLocale = state.pathParameters['locale'];
// if (currentLocale == null) {
// return '$defaultLanguage/${state.pathWithParameters}';
// }
// return null;
// }
//
// Iterable<GoRoute> get authRoutes => [
// GoRoute(
// path: 'Profile',
// redirect: (context, state) =>
// isLoggedIn ? null : '${context.language.name}/profile',
// builder: (context, state) => const ProfileRoute(),
// ),
// GoRoute(
// path: 'login',
// redirect: (context, state) =>
// isLoggedIn ? '${context.language.name}/profile' : null,
// builder: (context, state) => const ProfileRoute(),
// //builder: (context, state) => const LoginRoute(),
// ),
// // GoRoute(
// // path: 'register',
// // redirect: (context, state) =>
// // isLoggedIn ? '${context.language.name}/profile' : null,
// // builder: (context, state) => const RegisterRoute(),
// // ),
// ];
//
// static final Iterable<GoRoute> bottomBarRoutes = BottomNavBarItem.values.map(
// (e) => switch (e) {
// BottomNavBarItem.home => GoRoute(
// path: e.routePath,
// pageBuilder: (context, state) => const NoTransitionPage(
// child: HomeRoute(),
// ),
// ),
// BottomNavBarItem.uaeNumbers => GoRoute(
// path: e.routePath,
// pageBuilder: (context, state) => const NoTransitionPage(
// child: ListUAENumbersRoute(),
// ),
// routes: [
// ...IndicatorEnum.values.map(
// (ie) => GoRoute(
// path: ie.name,
// builder: (context, state) => switch (ie) {
// // environment
// IndicatorEnum.nationalReservesArea =>
// const AreaOfNationalReservesBody(),
// IndicatorEnum.crudeOilProduction => const OilBody(),
// IndicatorEnum.exportOilQuantity => const OilBody(),
// IndicatorEnum.electricityProduction =>
// const ElectricityProductionBody(),
// IndicatorEnum.electricityConsumption =>
// const ElectricityConsumptionBody(),
// IndicatorEnum.desalinatedWaterProduction => const WaterBody(),
// IndicatorEnum.municipalWaste => throw UnimplementedError(),
// // trade
// IndicatorEnum.inflationRate => const InflationBody(),
// IndicatorEnum.gdpConstant => const GdpBody(),
// IndicatorEnum.gdpGrowthConstant => const GdpBody(),
// IndicatorEnum.aircraftMovement => const AircraftBody(),
// IndicatorEnum.tradeValue => const TradeBody(),
// IndicatorEnum.hotelGuests => const HotelsBody(),
// // social
// IndicatorEnum.studentsGeneral => const GeneralEducationBody(),
// IndicatorEnum.studentsHigher => const HigherEducationBody(),
// IndicatorEnum.hospitalsGovernment => const HospitalsBody(),
// IndicatorEnum.hospitalsPrivate => const HospitalsBody(),
// IndicatorEnum.laborForce => const LaborForceBody(),
// IndicatorEnum.population => const PopulationBody(),
// },
// ),
// ),
// ],
// ),
// BottomNavBarItem.competitiveness => GoRoute(
// path: e.routePath,
// pageBuilder: (context, state) => const NoTransitionPage(
// child: ListReportsRoute(),
// ),
// routes: [
// GoRoute(
// path: ':reportCode',
// builder: (context, state) => ReportRoute(
// reportCode: state.pathParameters['reportCode'] as String,
// ),
// ),
// ],
// ),
// BottomNavBarItem.countryStatistics => GoRoute(
// path: e.routePath,
// pageBuilder: (context, state) => const NoTransitionPage(
// child: ListCountriesRoute(),
// ),
// routes: [
// GoRoute(
// path: ':alpha2CountryCode',
// redirect: (context, state) {
// final code = state.pathParameters['alpha2CountryCode'];
// if (code == null) return 'country-profile';
// return null;
// },
// builder: (context, state) {
// final alpha2Code = state.pathParameters['alpha2CountryCode']!;
// final country = ISOCountry.fromAlpha2(alpha2Code);
// return CountryProfileRoute(country: country);
// },
// ),
// ],
// ),
// },
// );
//
// Iterable<GoRoute> get _drawerRoutes => [
// GoRoute(
// path: 'profile',
// redirect: (context, state) {
// if (isLoggedIn) return null;
// return '/${context.language.name}/login';
// },
// pageBuilder: (context, state) => const NoTransitionPage(
// child: ProfileRoute(),
// ),
// routes: [
// GoRoute(
// path: 'reset-pw',
// builder: (context, state) => const ResetPwRoute(),
// ),
// ],
// ),
// ...DrawerItems.values.map(
// (e) => GoRoute(
// path: e.routePath,
// pageBuilder: (context, state) => NoTransitionPage(
// child: switch (e) {
// DrawerItems.favorites => const MyFavoritesRoute(),
// //DrawerItems.feedback => const FeedbackRoute(),
// DrawerItems.feedback => FeedbackForm(),
// DrawerItems.privacyPolicy => const PrivacyPolicyRoute(),
// DrawerItems.manageuser => ManageUserRouter()
// // DrawerItems.userGuide => const NotificationsRoute(),
// // DrawerItems.notifications => const UserGuideRoute(),
// },
// ),
// ),
// ),
// ];
// //
// ShellRoute get inAppRoutes => ShellRoute(
// builder: (context, state, child) => LoggedInHomeScaffold(
// body: child,
// ),
// routes: [
// ...bottomBarRoutes,
// ..._drawerRoutes,
// ],
// );
// }
// //
// String? _pathCache;
//
// @riverpod
// Future<GoRouter> router(RouterRef ref) async {
// final routerKey = GlobalKey<NavigatorState>(
// debugLabel: 'routerKey',
// );
// final isLoggedIn = await ref.watch(
// authUseCaseProvider.future.select(
// (session) async => (await session) != null,
// ),
// );
// final language = await ref.watch(
// preferencesUseCaseProvider.future.select(
// (asyncPrefs) async => (await asyncPrefs).language,
// ),
// );
// final myRouter = _MyRouter(
// isLoggedIn: isLoggedIn,
// );
// final router = GoRouter(
// navigatorKey: routerKey,
// initialLocation: _pathCache ?? '/$language/home',
// routes: <RouteBase>[
// GoRoute(
// redirect: _MyRouter.rootRedirect,
// path: '/:locale',
// routes: [
// myRouter.inAppRoutes,
// // ...myRouter.authRoutes,
// ]
// .map(
// (e) => ShellRoute(
// builder: (context, state, child) {
// final goRouterState = GoRouterState.of(context);
// final locale = goRouterState.pathParameters['locale']!;
// final language = LanguageLocale.fromName(locale);
// return Localizations.override(
// context: context,
// delegates: GlobalMaterialLocalizations.delegates,
// locale: Locale(
// switch (language) {
// LanguageLocale.enUS => 'en',
// LanguageLocale.arAE => 'ar',
// },
// ),
// child: Directionality(
// textDirection: switch (language) {
// LanguageLocale.enUS => TextDirection.ltr,
// LanguageLocale.arAE => TextDirection.rtl,
// },
// child: child,
// ),
// );
// },
// routes: [e],
// ),
// )
// .toList(),
// ),
// ],
// );
// ref.onDispose(
// () {
// _pathCache =
// ref.state.valueOrNull?.routeInformationProvider.value.uri.toString();
// router.dispose();
// },
// );
//
// return router;
// }
import 'dart:convert';
import 'package:flutter/material.dart';
@ -300,17 +6,19 @@ import 'package:go_router/go_router.dart';
import 'package:uae_stat/config/sessionCheckScreen.dart';
import 'package:uae_stat/domain/entities/session_entity.dart';
import 'package:uae_stat/infrastructure/data/p_auth_repo.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Competitiveness/Detailedcompetitiveness.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/DetailedCountryProfilePage.dart';
import 'package:uae_stat/presentation/Screens/app_tour/WelcomePage.dart';
import 'package:uae_stat/presentation/Screens/auth_verification/forgot_password.dart';
import 'package:uae_stat/presentation/Screens/auth_verification/privacy_policy.dart';
import 'package:uae_stat/presentation/Screens/auth_verification/registration.dart';
import 'package:uae_stat/presentation/Screens/auth_verification/terms&conditions.dart';
import 'package:uae_stat/presentation/Screens/charts/chart.dart';
import 'package:uae_stat/presentation/Screens/charts/screens/chart_screen.dart';
import 'package:uae_stat/presentation/Screens/demo_home2.dart';
import 'package:uae_stat/presentation/Screens/online_offline_verification/internet_check.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/Detailedcompetitiveness.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/Competitiveness/competitiveness.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/DetailedCountryProfilePage.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/UAE_Numbers/uae_numbers.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/contactUs.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/notification/notification.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/notification/notification_details.dart';
@ -325,21 +33,9 @@ import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_g
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/KeyFeatures/useTheApp.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/aboutApp.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/userguide.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart';
import 'package:uae_stat/presentation/Screens/Bottom Navigation Pages/Competitiveness/competitiveness.dart';
import 'package:uae_stat/presentation/Screens/Bottom Navigation Pages/Country_Profile/country_profile.dart';
import '../domain/use_cases/preferences_use_case.dart';
import '../presentation/Screens/auth_verification/changepassword.dart';
import '../presentation/Screens/auth_verification/confirm_password.dart';
import '../presentation/Screens/auth_verification/create_new_pw.dart';
import '../presentation/Screens/auth_verification/otp_verification.dart';
import '../presentation/Screens/demo_home.dart';
import '../presentation/Screens/profilepage.dart';
import '../presentation/routes/auth_routes/login_route.dart';
import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/bookmark.dart';
import '../presentation/routes/drawer_routes/Drawer Items/edit_profile.dart';
import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart';
@ -463,23 +159,23 @@ final GoRouter router = GoRouter(
);
},
),
GoRoute(
path: '/mailverification',
builder: (context, state) => EmailVerificationScreen(
email: '',
userId: '',
otp: '',
otpId: '',
sendVerificationCode: (String) {},
),
),
GoRoute(
path: '/changepassword/:userId',
builder: (context, state) {
final userId = state.pathParameters['userId']!;
return Changepassword(userId: userId);
},
),
// GoRoute(
// path: '/mailverification',
// builder: (context, state) => EmailVerificationScreen(
// email: '',
// userId: '',
// otp: '',
// otpId: '',
// sendVerificationCode: (String) {},
// ),
// ),
// GoRoute(
// path: '/changepassword/:userId',
// builder: (context, state) {
// final userId = state.pathParameters['userId']!;
// return Changepassword(userId: userId);
// },
// ),
GoRoute(
path: '/forgot-password/:email',
builder: (context, state) {
@ -497,13 +193,13 @@ final GoRouter router = GoRouter(
return CreateNewPw(userId: userId, email: email, keyParam: key);
},
),
GoRoute(
path: '/confirmpasswd',
builder: (context, state) => ConfirmPassword(
email: '',
userId: '',
),
),
// GoRoute(
// path: '/confirmpasswd',
// builder: (context, state) => ConfirmPassword(
// email: '',
// userId: '',
// ),
// ),
GoRoute(
path: '/termsandconditions',
builder: (context, state) => TermsOfUse(),
@ -648,11 +344,6 @@ final GoRouter router = GoRouter(
builder: (context, state) => Competitiveness(),
),
GoRoute(
path: '/competitiveness',
builder: (context, state) => Competitiveness(),
),
GoRoute(
path: '/countryprofile',
builder: (context, state) => const ListCountriesRoute(),

View File

@ -1,17 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/logo_asset_path.dart';
class Welcomepage extends StatelessWidget {
class Welcomepage extends ConsumerStatefulWidget {
const Welcomepage({super.key});
@override
ConsumerState<Welcomepage> createState() => _WelcomepageState();
}
class _WelcomepageState extends ConsumerState<Welcomepage> {
@override
Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height;
double mywidth = MediaQuery.of(context).size.width;
final isDarkTheme = ref.read(themeProvider) == ThemeMode.dark ||
(ref.read(themeProvider) == ThemeMode.system &&
MediaQuery.of(context).platformBrightness == Brightness.dark);
final fcscBanner = Image.asset(
BannerAssetPath.fcscLogoLight,
height: 40,
@ -26,26 +37,44 @@ class Welcomepage extends StatelessWidget {
body: Container(
width: mywidth,
height: myheight,
padding: EdgeInsets.zero,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
color: Colors.white,
color: isDarkTheme ? Colors.black : Colors.white,
child: Column(
children: [
Container(
padding: EdgeInsets.only(top: myheight*0.05,left: mywidth*0.2,right: mywidth*0.2,bottom: 0),
height: myheight > 500 ? myheight * 0.35 : 230,
padding: EdgeInsets.only(
top: myheight * 0.05,
left: mywidth * 0.2,
right: mywidth * 0.2,
bottom: 0),
height: myheight > 500 ? myheight * 0.35 : 230,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text( context.translate('Welcome to','مرحبًا بك في'),
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'Roboto',fontSize: 30,fontWeight: FontWeight.normal,color: Colors.black,),),
SizedBox(height: 6,),
uaestat,
SizedBox(height: 6,),
Text(context.translate('Your Ultimate statistics Companion','رفيقك الأمثل للإحصائيات'),
Text(
context.translate('Welcome to', 'مرحبًا بك في'),
textAlign: TextAlign.center,
style: TextStyle(fontFamily: 'Roboto',fontSize: 22,color: Color(0xFF898C81)),),
style: TextStyle(
fontFamily: 'Roboto',
fontSize: 30,
fontWeight: FontWeight.normal,
color: isDarkTheme ? Colors.white : Colors.black,
),
),
const SizedBox(height: 6),
uaestat,
const SizedBox(height: 6),
Text(
context.translate('Your Ultimate statistics Companion',
'رفيقك الأمثل للإحصائيات'),
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Roboto',
fontSize: 22,
color: isDarkTheme ? Color(0xFF898C81) : Color(0xFF898C81),
),
),
],
),
),
@ -53,32 +82,46 @@ class Welcomepage extends StatelessWidget {
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
width: mywidth,
height: myheight*0.5,
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage('assets/app_tour/welcome2.png'),
height: myheight * 0.5,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/app_tour/welcome2.png'),
fit: BoxFit.fitWidth,
),
),
child: Padding(
padding: EdgeInsets.only(bottom: 40),
padding: const EdgeInsets.only(bottom: 40),
child: Align(
alignment: Alignment.bottomCenter,
child: ElevatedButton(onPressed: (){
context.go('/myhomepage');
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Color(0xFF90B0D5)),
),
child: Text(context.translate("Let's get Started",'لنبدأ'),
style: TextStyle(color: Colors.white, fontFamily: 'Roboto',fontSize: 16),
child: ElevatedButton(
onPressed: () {
context.go('/myhomepage');
},
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all(const Color(0xFF90B0D5)),
),
child: Text(
context.translate("Let's get Started", 'لنبدأ'),
style: const TextStyle(
color: Colors.white,
fontFamily: 'Roboto',
fontSize: 16,
),
),
),
),
),
),
Center(
child: fcscBanner,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 50),
child: Image.asset(
isDarkTheme
? BannerAssetPath.fcscLogoDark
: BannerAssetPath.fcscLogoLight,
height: 30,
),
)
],
),
),
@ -86,4 +129,3 @@ class Welcomepage extends StatelessWidget {
);
}
}

View File

@ -1,318 +0,0 @@
import 'dart:math';
import 'dart:convert';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/api_config.dart';
import 'otp_verification.dart';
class Changepassword extends StatefulWidget {
// final String email;
final String userId;
const Changepassword({Key? key, required this.userId});
@override
State<Changepassword> createState() => _ResetPasswordScreenState();
}
class _ResetPasswordScreenState extends State<Changepassword> {
final _pb = PocketBase(apiUrl);
// final pb = PocketBase('http://127.0.0.1:8090');
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
bool _isLoading = false;
String? _otpId;
String? _verificationCode;
Future<void> sendVerificationCode(String email) async {
if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});
final email = _emailController.text.trim();
//print('Email - $email');
try {
// Authenticate admin to get the token
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final authToken = adminAuth.token;
//print('Admin Token: $authToken');
// Check if email exists in the user collection
final userCheckResponse = await http.get(
Uri.parse(
'$apiUrl/api/collections/users/records?filter=email="$email"'),
headers: {
'Authorization': 'Bearer $authToken',
},
);
if (userCheckResponse.statusCode == 200) {
final userData = jsonDecode(userCheckResponse.body);
//print('userDetails ->: $userData');
if (userData['items'].isNotEmpty) {
// Email exists; retrieve user ID
// final userId = userData['items'][0]['id'];
// Proceed with OTP request
final otpResponse = await http.post(
Uri.parse('$apiUrl/api/collections/otp_requests/records'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $authToken',
},
body: jsonEncode({
'email': email,
}),
);
if (otpResponse.statusCode == 200) {
//print('otpResponse- ${otpResponse.body} ');
final otpData = jsonDecode(otpResponse.body);
final otp = otpData['otp']; // OTP value from the response
final otpId = otpData['id'];
_otpId = otpData['id'];
//print('OTP: $otp, ID: $otpId, ');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Verification code sent to $email")),
);
// Navigate to the Email Verification screen
context.go(
'/mailverification',
extra: {
'email': email,
'userId': widget.userId, // Pass user ID to the next screen
'otp': otp,
'otpId': otpId,
'sendVerificationCode': sendVerificationCode,
},
);
} else {
final error =
jsonDecode(otpResponse.body)['error'] ?? "Unknown error";
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $error")),
);
}
} else {
// Email not found
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Please Enter Registered Mail ID")),
);
}
} else {
// Error in user collection request
final error =
jsonDecode(userCheckResponse.body)['error'] ?? "Unknown error";
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $error")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Failed to process request: $e")),
);
} finally {
setState(() {
_isLoading = false;
});
}
}
}
@override
void initState() {
super.initState();
print(widget.userId);
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
double screenheight = MediaQuery.of(context).size.height;
double screenwidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: const Text(
'Reset Password',
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w300,
color: Colors.black87,
),
),
),
const SizedBox(height: 8),
Center(
child: const Text(
'Please Enter Your Email Address To\nReceive a verification Code',
style: TextStyle(
fontSize: 14,
color: Colors.black87,
fontWeight: FontWeight.w400,
height: 1.5,
),
),
),
const SizedBox(height: 24),
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'Enter your email',
hintStyle: const TextStyle(
color: Color(0xFFC3C6CB),
fontSize: 14,
),
prefixIcon: const Icon(
Icons.email_outlined,
color: Colors.blue,
size: 20,
),
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.black12,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.black12,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Color(0xFF8B7355),
width: 1,
),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
},
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isLoading
? null
: () {
final email = _emailController.text.trim();
sendVerificationCode(email);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8B7355),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isLoading)
Container(
child: const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
)
else ...[
const Text(
'Send',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
SizedBox(
width: 2,
),
Icon(
Icons.arrow_forward_ios,
color: Colors.white38,
size: 18,
),
],
],
),
),
),
],
),
SizedBox(
height: screenheight / 3,
),
Center(
child: Container(
height: screenheight / 8,
width: screenwidth / 2,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/splash_screen/logo.png"), // Background image asset
fit: BoxFit.fill,
),
),
))
],
),
),
),
),
),
);
}
}

View File

@ -1,282 +0,0 @@
import 'package:flutter/material.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/presentation/Screens/profilepage.dart';
class ConfirmPassword extends StatefulWidget {
final String email;
final String userId;
ConfirmPassword({required this.email, required this.userId});
@override
State<ConfirmPassword> createState() => _ConfirmPasswordState();
}
class _ConfirmPasswordState extends State<ConfirmPassword> {
final _pb = PocketBase(apiUrl);
// final pb = PocketBase('http://127.0.0.1:8090');
final _formKey = GlobalKey<FormState>();
bool _obscurePassword = true;
bool _obscureConfirmPassword = true;
String? _password;
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Required';
} else if (value.length < 6) {
return 'Password must be at least 6 characters';
}
_password = value; // Store the password for confirm password validation
return null;
}
String? _validateConfirmPassword(String? value) {
if (value == null || value.isEmpty) {
return 'Required';
} else if (value != _password) {
return 'Passwords do not match';
}
return null;
}
// Function to check and update password
Future<void> updatePassword(String userId, String newPassword) async {
try {
//print('userRecord- $userId');
// Authenticate as admin
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final token = adminAuth.token;
final headers = {
'Authorization': 'Bearer $token',
};
final userRecord = await _pb.collection('users').getOne(userId);
//print('userRecord- $userRecord');
final oldPassword = userRecord.data['password'];
print(oldPassword);
// Check if the new password is the same as the old password
if (oldPassword == newPassword) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'New password is similar to the old password. Please try a different one.'),
backgroundColor: Colors.orange,
),
);
return; // Exit early
}
// Update password
await _pb.collection('users').update(
userId, // User ID
body: {
'password': newPassword,
'passwordConfirm': newPassword,
}, // Updated password
headers: headers,
);
// Show success Snackbar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Password updated successfully'),
backgroundColor: Colors.blue,
),
);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen(userId: userId)),
);
} catch (e) {
print('Error updating password: $e');
// Show error Snackbar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to update password'),
backgroundColor: Colors.red,
),
);
}
}
@override
Widget build(BuildContext context) {
double screenheight = MediaQuery.of(context).size.height;
double screenwidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: SafeArea(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
SizedBox(
height: screenheight / 6,
),
Text(
"Create New Password",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: screenheight / 35,
),
Text(
"Your new password must de different\nform previously used password",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
SizedBox(
height: screenheight / 35,
),
TextFormField(
obscureText: _obscurePassword,
decoration: InputDecoration(
hintText: 'Enter your password',
hintStyle: const TextStyle(
color: Color(0xFFC3C6CB),
fontSize: 14,
),
prefixIcon: Icon(
Icons.lock,
color: Colors.blue,
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
color: Colors.blue,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: OutlineInputBorder(),
),
validator: _validatePassword,
),
SizedBox(
height: screenheight / 35,
),
TextFormField(
obscureText: _obscureConfirmPassword,
decoration: InputDecoration(
hintText: 'Confirm password',
hintStyle: const TextStyle(
color: Color(0xFFC3C6CB),
fontSize: 14,
),
prefixIcon: Icon(
Icons.lock,
color: Colors.blue,
),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword
? Icons.visibility_off
: Icons.visibility,
color: Colors.blue,
),
onPressed: () {
setState(() {
_obscureConfirmPassword =
!_obscureConfirmPassword;
});
},
),
border: OutlineInputBorder(),
),
validator: _validateConfirmPassword,
),
SizedBox(
height: screenheight / 35,
),
SizedBox(
width: screenwidth / 1.1,
child: ElevatedButton(
onPressed: () async {
if ((_formKey.currentState?.validate() ?? false)) {
//String userId = '4hai9cbn4lg6jt4'; // Replace with the actual user ID
String newPassword = _password ??
''; // Replace with the new password
//print("Confirm Passwd- $widget.userId, $newPassword");
await updatePassword(widget.userId, newPassword);
}
// Add verification logic here
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.brown[300],
padding: EdgeInsets.symmetric(
horizontal: 80, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Save",
style: TextStyle(
fontSize: 18, color: Colors.white),
),
SizedBox(
width: 2,
),
Icon(
Icons.arrow_forward_ios,
color: Colors.white38,
size: 18,
),
],
),
),
),
],
),
),
SizedBox(
height: screenheight / 15,
),
Center(
child: Container(
height: screenheight / 10,
width: screenwidth / 2.5,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/splash_screen/logo.png"), // Background image asset
fit: BoxFit.fill,
),
),
))
],
),
),
),
),
);
}
}

View File

@ -1,279 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/api_config.dart';
import 'confirm_password.dart';
class EmailVerificationScreen extends StatefulWidget {
final String email;
final String userId, otp, otpId;
final Function(String) sendVerificationCode;
EmailVerificationScreen(
{required this.email,
required this.userId,
required this.otp,
required this.otpId,
required this.sendVerificationCode});
@override
_EmailVerificationScreenState createState() =>
_EmailVerificationScreenState();
}
class _EmailVerificationScreenState extends State<EmailVerificationScreen> {
// final pb = PocketBase('http://127.0.0.1:8090');
final _pb = PocketBase(apiUrl);
final List<TextEditingController> _otpControllers =
List.generate(4, (_) => TextEditingController());
int _secondsRemaining = 120; // 2 minutes timer
late Timer _timer;
bool _canResend = false;
@override
void initState() {
super.initState();
_startTimer();
}
void _startTimer() {
_canResend = false;
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (_secondsRemaining > 0) {
setState(() {
_secondsRemaining--;
});
} else {
setState(() {
_canResend = true; // Enable "Resend Code" when the timer ends
});
timer.cancel();
}
});
}
String get _formattedTime {
final minutes = _secondsRemaining ~/ 60;
final seconds = _secondsRemaining % 60;
return '$minutes:${seconds.toString().padLeft(2, '0')}';
}
@override
void dispose() {
_timer.cancel();
for (var controller in _otpControllers) {
controller.dispose();
}
super.dispose();
}
void verify(BuildContext context, String email, String enteredOTP,
String userId) async {
// Validate OTP length
if (enteredOTP.length != 4) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Please enter a valid 4-digit code!")),
);
return;
}
try {
if (enteredOTP == widget.otp && email == widget.email) {
// Parse the response
//print('VERIFIED OTF');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConfirmPassword(
email: widget.email, // Pass the email
userId: widget.userId)),
);
} else {
// No matching record found
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Invalid OTP. Please try again.")),
);
}
} catch (e) {
// Catch network or other unexpected errors
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Something went wrong. Please try again.")),
);
}
}
@override
Widget build(BuildContext context) {
double screenheight = MediaQuery.of(context).size.height;
double screenwidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
SizedBox(
height: screenheight / 6,
),
Text(
"Verify Your Email",
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
SizedBox(height: 8),
Text(
"Please Enter the 4 digit Code Sent to\nrjosh289778@gmail.com",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(4, (index) {
return Container(
width: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
border:
Border.all(color: Colors.grey.shade400, width: 1),
borderRadius: BorderRadius.circular(8),
),
child: TextField(
controller: _otpControllers[index],
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
maxLength: 1,
decoration: InputDecoration(
counterText: "",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Colors.blue, width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
BorderSide(color: Colors.blue, width: 2),
),
),
onChanged: (value) {
if (value.length == 1 && index < 3) {
FocusScope.of(context).nextFocus();
}
},
),
);
}),
),
SizedBox(height: 16),
Text.rich(
TextSpan(
text: "Code Expires in ",
style: TextStyle(color: Colors.grey[600]),
children: [
TextSpan(
text: _formattedTime,
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.black87),
),
],
),
),
SizedBox(height: 5),
TextButton(
onPressed: _canResend
? () {
setState(() {
_secondsRemaining = 120;
_startTimer();
});
widget.sendVerificationCode(widget.email);
}
: null,
child: Text(
"Resend Code",
style: TextStyle(
color: Colors.brown, fontWeight: FontWeight.bold),
),
),
SizedBox(height: 5),
SizedBox(
width: screenwidth / 1.2,
child: ElevatedButton(
onPressed: () async {
// Combine the entered OTP
String enteredOTP = _otpControllers
.map((controller) => controller.text)
.join();
verify(
context, widget.email, enteredOTP, widget.userId);
// Add verification logic here
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.brown[300],
padding:
EdgeInsets.symmetric(horizontal: 80, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Verify",
style: TextStyle(fontSize: 18, color: Colors.white),
),
SizedBox(
width: 3,
),
Icon(
Icons.arrow_forward_ios,
color: Colors.white38,
size: 18,
),
],
),
),
),
],
),
SizedBox(
height: screenheight / 6,
),
Center(
child: Container(
height: screenheight / 8,
width: screenwidth / 2,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/splash_screen/logo.png"), // Background image asset
fit: BoxFit.fill,
),
),
))
],
),
),
),
);
}
}

View File

@ -232,7 +232,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
});
try {
final adminAuth = await pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
.authWithPassword('kannan@sightspectrum.com', '1DgZ_we3k5UuCZiF9wziwECQzQo1YNRQ');
final adminToken = adminAuth.token;
print('adminTokenREgistration- ${adminToken}');

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
class ChartData {
final String xLabel;
final double yValue;
ChartData({required this.xLabel, required this.yValue});
factory ChartData.fromJson(Map<String, dynamic> json) {
return ChartData(
xLabel: json['x_label'] as String,
yValue: (json['y_value'] as num).toDouble(),
);
}
}

View File

@ -1,424 +0,0 @@
// import 'dart:convert';
// import 'package:flutter/material.dart';
// import 'package:go_router/go_router.dart';
// import 'package:intl/intl.dart';
// import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package
// import 'package:http/http.dart' as http;
//
// void main() {
// runApp(MyApp());
// }
//
// class MyApp extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// home: ChartPage(dataSets: ''),
// );
// }
// }
//
// class ChartPage extends StatefulWidget {
// final String dataSets;
// const ChartPage({Key? key, required this.dataSets});
//
// @override
// ChartPageState createState() => ChartPageState();
// }
//
// class ChartPageState extends State<ChartPage> {
// List<dynamic> chartsData = [];
// bool isLoading = true;
//
// @override
// void initState() {
// super.initState();
// fetchChartData(widget.dataSets);
// }
//
// Future<void> fetchChartData(datasets) async {
// const baseUrl =
// 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL
// final url = Uri.parse('$baseUrl?dataset=$datasets');
//
// try {
// final response = await http.get(url);
// if (response.statusCode == 200) {
// setState(() {
// chartsData = jsonDecode(response.body);
// isLoading = false;
// });
// } else {
// throw Exception('Failed to load data');
// }
// } catch (error) {
// setState(() {
// isLoading = false;
// });
// print('Error fetching data: $error');
// }
// }
//
// List<ChartData> parseLineChartData(List<dynamic> response) {
// // Group by timePeriod (year) and sum the values
// Map<String, double> groupedData = {};
//
// response.forEach((entry) {
// final year = entry['ObsKey']['TIME_PERIOD']?.toString() ?? 'Unknown';
// final value =
// double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ??
// 0.0;
//
// // Add the value to the existing year (if exists) or initialize it
// if (groupedData.containsKey(year)) {
// groupedData[year] = groupedData[year]! + value;
// } else {
// groupedData[year] = value;
// }
// });
//
// // Sort the grouped data by year in ascending order
// var sortedEntries = groupedData.entries.toList()
// ..sort((a, b) => int.parse(a.key).compareTo(int.parse(b.key)));
//
// // Convert the sorted data to a list of ChartData
// return sortedEntries
// .map((entry) => ChartData(
// timePeriod: entry.key,
// value: entry.value,
// value1: 0.0)) // Add value1 as 0.0
// .toList();
// }
//
// List<ChartData> parseBarChartData(List<dynamic> response) {
// return response
// .asMap()
// .entries
// .map((entry) => ChartData(
// timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? 'Unknown',
// value: double.tryParse(
// entry.value['ObsValue']['Value'].toString()) ??
// 0.0,
// value1: 0.0, // Ensure value1 is passed as well
// ))
// .toList();
// }
//
// List<ChartData> parseColumnChartData(List<dynamic> response) {
// List<ChartData> chartDataList = [];
//
// // Temporary map to store values by TIME_PERIOD
// Map<String, ChartData> timePeriodMap = {};
//
// for (var entry in response) {
// String timePeriod = entry['ObsKey']['TIME_PERIOD'].toString();
// double value =
// double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ??
// 0.0;
// String indicator = entry['ObsKey']['H_INDICATOR'];
//
// // If the entry is for TOR, map it to 'value'
// if (indicator == 'TOR') {
// if (timePeriodMap.containsKey(timePeriod)) {
// // Update the value by creating a new ChartData object
// timePeriodMap[timePeriod] = ChartData(
// timePeriod: timePeriod,
// value: value, // Set the new value
// value1: timePeriodMap[timePeriod]?.value1 ?? 0.0,
// );
// } else {
// timePeriodMap[timePeriod] = ChartData(
// timePeriod: timePeriod,
// value: value,
// value1: 0.0,
// );
// }
// }
//
// // If the entry is for TAR, map it to 'value1'
// if (indicator == 'TAR') {
// if (timePeriodMap.containsKey(timePeriod)) {
// // Create a new ChartData object with the updated value1
// timePeriodMap[timePeriod] = ChartData(
// timePeriod: timePeriod,
// value: timePeriodMap[timePeriod]?.value ??
// 0.0, // Retain the current value
// value1: value, // Update the value1
// );
// } else {
// timePeriodMap[timePeriod] = ChartData(
// timePeriod: timePeriod,
// value: 0.0,
// value1: value,
// );
// }
// }
// }
//
// // Convert the map to a list
// chartDataList = timePeriodMap.values.toList();
//
// return chartDataList;
// }
//
// Widget buildChart(dynamic chartData) {
// print('chartData $chartData');
// switch (chartData['chart_type']) {
// case 'area':
// return SfCartesianChart(
// primaryXAxis: CategoryAxis(
// title: AxisTitle(text: 'Year'),
// labelRotation: 45, // Rotate labels if they overlap
// ),
// title: ChartTitle(text: 'Hotel Estabhlishments'),
// legend: Legend(isVisible: true),
// tooltipBehavior: TooltipBehavior(
// enable: true,
// builder: (dynamic data, dynamic point, dynamic series,
// int pointIndex, int seriesIndex) {
// return Container(
// padding: const EdgeInsets.all(8),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(5),
// ),
// child: Text(
// '${data.timePeriod}: ${formatNumber(data.value)}',
// style: const TextStyle(color: Colors.black),
// ),
// );
// },
// ),
// series: <CartesianSeries>[
// // AreaSeries for the area chart
// AreaSeries<ChartData, String>(
// dataSource: parseLineChartData(
// chartData['response']), // your chart data
// color: Color(0xFF7DAFBC), // Area color
// borderDrawMode: BorderDrawMode
// .excludeBottom, // Optional, adjust border settings
// borderColor: Colors.green, // Border color
// borderWidth: 2, // Border width
// xValueMapper: (ChartData data, _) =>
// data.timePeriod, // Map x to DateTime
// yValueMapper: (ChartData data, _) => data.value,
// // name: chartData['dataset'],
// name: 'Hotels',
// dataLabelSettings: DataLabelSettings(
// isVisible: true,
// builder: (dynamic data, dynamic point, dynamic series,
// int pointIndex, int seriesIndex) {
// return Text(
// formatNumber(data.value),
// style: const TextStyle(fontSize: 12, color: Colors.black),
// );
// },
// ), // Map y to numerical value
// )
// ]);
// case 'bar':
// return SfCartesianChart(
// primaryXAxis: CategoryAxis(),
// title: ChartTitle(text: 'Hotel Occupancy Rate'),
// tooltipBehavior: TooltipBehavior(
// enable: true,
// builder: (dynamic data, dynamic point, dynamic series,
// int pointIndex, int seriesIndex) {
// return Container(
// padding: const EdgeInsets.all(8),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(5),
// ),
// child: Text(
// '${data.timePeriod}: ${formatNumber(data.value)}',
// style: const TextStyle(color: Colors.black),
// ),
// );
// },
// ),
// series: <CartesianSeries<ChartData, String>>[
// BarSeries<ChartData, String>(
// dataSource: parseBarChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => data.timePeriod,
// yValueMapper: (ChartData data, _) => data.value,
// name: chartData['dataset'],
// color: Color(0xFF7DAFBC),
// dataLabelSettings: DataLabelSettings(
// isVisible: true,
// builder: (dynamic data, dynamic point, dynamic series,
// int pointIndex, int seriesIndex) {
// return Text(
// formatNumber(data.value),
// style: const TextStyle(fontSize: 12, color: Colors.black),
// );
// },
// ),
// // isTrackVisible: true,
// // trackColor: Colors.red
// ),
// ],
// );
// case 'column':
// return SfCartesianChart(
// primaryXAxis: CategoryAxis(
// title: AxisTitle(text: 'Year'),
// labelRotation: 45, // Rotate labels if they overlap
// ),
// primaryYAxis: NumericAxis(
// title: AxisTitle(text: 'No of rooms'),
// ),
// legend: Legend(isVisible: true),
// title: ChartTitle(text: 'Hotel Occupancy Rate'),
// enableSideBySideSeriesPlacement: true,
// tooltipBehavior: TooltipBehavior(enable: true),
// series: <CartesianSeries<ChartData, int>>[
// // First Series (value)
//
// ColumnSeries<ChartData, int>(
// name: 'Available Rooms',
// // opacity: 0.9,
// // width: 0.4,
// color: Color(0xFF587BA3),
// dataSource: parseColumnChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => int.parse(data.timePeriod),
// yValueMapper: (ChartData data, _) => data.value1,
// ),
// // Second Series (value1)
// ColumnSeries<ChartData, int>(
// color: Color(0xFF90B0D5),
// name: 'Occupied Rooms',
// dataSource: parseColumnChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => int.parse(data.timePeriod),
// yValueMapper: (ChartData data, _) => data.value,
// ),
// ], // Numeric Y-axis to plot the values
// );
// default:
// return Center(child: Text('Unknown chart type'));
// }
// }
//
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// backgroundColor: const Color(0xFF7DAFBC),
// appBar: AppBar(
// backgroundColor: const Color(0xFF7DAFBC),
// elevation: 0,
// leading: IconButton(
// icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white),
// onPressed: () {
// context.go('/myhomepage');
// },
// ),
// title: const Text(
// 'Charts',
// style: TextStyle(color: Colors.white),
// ),
// ),
// body: isLoading
// ? const Center(child: CircularProgressIndicator())
// : Column(
// children: [
// Card(
// margin: const EdgeInsets.all(10),
// color: Colors.white,
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: const [
// Text(
// 'Card 1 Title',
// style: TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold),
// ),
// SizedBox(height: 10),
// Text('Content for the first card goes here.'),
// ],
// ),
// ),
// ),
// Card(
// margin: const EdgeInsets.all(10),
// color: Colors.white,
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: const [
// Text(
// 'Card 2 Title',
// style: TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold),
// ),
// SizedBox(height: 10),
// Text('Content for the second card goes here.'),
// ],
// ),
// ),
// ),
// Expanded(
// child: ListView.builder(
// itemCount: chartsData.length,
// itemBuilder: (context, index) {
// return Card(
// margin: const EdgeInsets.all(10),
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container(
// height: 300,
// child: buildChart(chartsData[index]),
// ),
// ],
// ),
// ),
// );
// },
// ),
// ),
// ],
// ),
// );
// }
// }
//
// class ChartData {
// final String timePeriod;
// final double value;
// final double value1;
// final String formattedValue;
//
// ChartData(
// {required this.timePeriod, required this.value, required this.value1})
// : formattedValue = formatNumber(value);
// }
//
// // String formatNumber(double value) {
// // if (value >= 1e12) {
// // return '${(value / 1e12).toStringAsFixed(2)} T'; // Trillions
// // } else if (value >= 1e9) {
// // return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions
// // } else if (value >= 1e6) {
// // return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions
// // } else if (value >= 1e5) {
// // return '${(value / 1e5).toStringAsFixed(2)} L'; // Lakhs
// // } else if (value >= 1e3) {
// // return '${(value / 1e3).toStringAsFixed(2)} K'; // Thousands
// // }
// // return value.toStringAsFixed(2); // Default to two decimal places
// // }
//
// String formatNumber(double value) {
// if (value >= 1e9) {
// return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions
// } else if (value >= 1e6) {
// return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions
// }
// return value
// .toStringAsFixed(2); // Default to two decimal places for smaller numbers
// }

View File

@ -1,328 +0,0 @@
// import 'dart:convert';
// import 'package:flutter/material.dart';
// import 'package:go_router/go_router.dart';
// import 'package:intl/intl.dart';
// import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package
// import 'package:http/http.dart' as http;
//
// void main() {
// runApp(MyApp());
// }
//
// class MyApp extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// home: ChartPage2(dataSets: ''),
// );
// }
// }
//
// class ChartPage2 extends StatefulWidget {
// final String dataSets;
// const ChartPage2({Key? key, required this.dataSets});
// @override
// ChartPageState createState() => ChartPageState();
// }
//
// class ChartPageState extends State<ChartPage2> {
// List<dynamic> chartsData = [];
// bool isLoading = true;
//
// @override
// void initState() {
// super.initState();
// fetchChartData(widget.dataSets);
// }
//
// Future<void> fetchChartData(dataSets) async {
// const baseUrl =
// 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL
// final url = Uri.parse('$baseUrl?dataset=$dataSets');
//
// try {
// final response = await http.get(url);
// if (response.statusCode == 200) {
// setState(() {
// chartsData = jsonDecode(response.body);
// isLoading = false;
// });
// } else {
// throw Exception('Failed to load data');
// }
// } catch (error) {
// setState(() {
// isLoading = false;
// });
// print('Error fetching data: $error');
// }
// }
//
// List<ChartData> parseLineChartData(List<dynamic> response) {
// return response.map((entry) {
// // Attempt to fetch the TIMEPERIOD
// final rawTimePeriod =
// entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD'];
// final formattedTimePeriod = rawTimePeriod != null
// ? rawTimePeriod.toString() // Use as-is if valid
// : 'Unknown'; // Fallback value if null
//
// // Parse the value or fallback to 0.0
// final value =
// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
// 0.0;
//
// return ChartData(
// timePeriod: formattedTimePeriod,
// value: value,
// );
// }).toList();
// }
//
// List<ChartData> parseBarChartData(List<dynamic> response) {
// return response
// .asMap()
// .entries
// .map((entry) => ChartData(
// timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ??
// 'Unknown', // Fallback to 'Unknown' if null
// value: double.tryParse(
// entry.value['ObsValue']['Value'].toString()) ??
// 0.0, // Handle null/invalid value
// ))
// .toList();
// }
//
// // Update parseColumnChartData for column chart
// List<ChartData> parseColumnChartData(List<dynamic> response) {
// return response.map((entry) {
// // Extract TIME_PERIOD as x
// final x =
// int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0;
// // Extract Value as y
// final y =
// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
// 0.0;
// return ChartData(xInt: x, y: y);
// }).toList();
// }
//
// // Update parseScatterChartData for scatter chart
// List<ChartData> parseScatterChartData(List<dynamic> response) {
// return response.map((entry) {
// // Parse TIME_PERIOD into DateTime
// final timePeriod = entry['ObsKey']?['TIME_PERIOD'];
// final x = timePeriod != null
// ? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime
// : DateTime.now(); // Fallback to current date if null
//
// // Parse Value into double
// final y =
// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
// 0.0;
//
// return ChartData(xDateTime: x, y: y);
// }).toList();
// }
//
// Widget buildChart(dynamic chartData) {
// switch (chartData['chart_type']) {
// case 'scatter':
// return SfCartesianChart(
// primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis
// tooltipBehavior: TooltipBehavior(enable: true),
// title: ChartTitle(text: 'Divorces'),
// series: <CartesianSeries>[
// ScatterSeries<ChartData, DateTime>(
// dataSource: parseScatterChartData(chartData['response']),
// xValueMapper: (ChartData data, _) =>
// data.xDateTime ?? DateTime.now(),
// yValueMapper: (ChartData data, _) => data.y,
// name: chartData['dataset'],
// dataLabelSettings: DataLabelSettings(isVisible: true),
// ),
// ],
// );
// case 'column':
// return SfCartesianChart(
// primaryXAxis: NumericAxis(
// title: AxisTitle(text: 'Year'),
// interval: 1, // Ensure no fractional intervals
// ),
// title: ChartTitle(text: 'Marriages'),
// series: <CartesianSeries<ChartData, int>>[
// ColumnSeries<ChartData, int>(
// dataSource: parseColumnChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => data.xInt ?? 0,
// yValueMapper: (ChartData data, _) => data.y,
// name: chartData['dataset'],
// color: Color(0xFF7DAFBC),
// width: 0.8,
// spacing: 0.2,
// dataLabelSettings:
// DataLabelSettings(isVisible: true), // Show data labels
// ),
// ],
// );
//
// case 'line':
// return SfCartesianChart(
// primaryXAxis: CategoryAxis(),
// title: ChartTitle(text: 'Line Chart'),
// legend: Legend(isVisible: true),
// tooltipBehavior: TooltipBehavior(enable: true),
// series: <CartesianSeries<ChartData, String>>[
// LineSeries<ChartData, String>(
// dataSource: parseLineChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => data.timePeriod ?? '',
// yValueMapper: (ChartData data, _) => data.value ?? 0.0,
// name: 'Sales',
// dataLabelSettings: DataLabelSettings(isVisible: true),
// ),
// ],
// );
// case 'bar':
// return SfCartesianChart(
// primaryXAxis: CategoryAxis(),
// title: ChartTitle(text: 'Bar Chart'),
// tooltipBehavior: TooltipBehavior(enable: true),
// series: <CartesianSeries<ChartData, String>>[
// BarSeries<ChartData, String>(
// dataSource: parseBarChartData(chartData['response']),
// xValueMapper: (ChartData data, _) => data.timePeriod ?? '',
// yValueMapper: (ChartData data, _) => data.value ?? 0.0,
// name: 'Gold',
// color: Color.fromRGBO(8, 142, 255, 1),
// ),
// ],
// );
//
// default:
// return Center(child: Text('Unknown chart type'));
// }
// }
//
// @override
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// backgroundColor: const Color(0xFF7DAFBC),
// appBar: AppBar(
// backgroundColor: Color(0xFF7DAFBC),
// elevation: 0,
// leading: IconButton(
// icon: Icon(Icons.arrow_back_ios_new, color: Colors.white),
// onPressed: () {
// context.go('/myhomepage');
// },
// ),
// title: Text(
// 'Charts',
// style: TextStyle(color: Colors.white),
// ),
// ),
// body: isLoading
// ? Center(child: CircularProgressIndicator())
// : Column(
// children: [
// // Row for two cards
// Row(
// children: [
// // First Card
// Expanded(
// flex: 6,
// child: Card(
// margin: EdgeInsets.all(10),
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// 'Card 1',
// style: TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold),
// ),
// SizedBox(height: 10),
// Text('Content for Card 1 goes here.'),
// ],
// ),
// ),
// ),
// ),
// // Second Card
// Expanded(
// flex: 6,
// child: Card(
// margin: EdgeInsets.all(10),
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// 'Card 2',
// style: TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold),
// ),
// SizedBox(height: 10),
// Text('Content for Card 2 goes here.'),
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// // List of charts
// Expanded(
// child: ListView.builder(
// itemCount: chartsData.length,
// itemBuilder: (context, index) {
// return Card(
// margin: EdgeInsets.all(10),
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// SizedBox(height: 10),
// Container(
// height: 300,
// child: buildChart(chartsData[index]),
// ),
// ],
// ),
// ),
// );
// },
// ),
// ),
// ],
// ),
// );
// }
// }
//
// class ChartData {
// final int? xInt; // For column and other charts, using int for x
// final DateTime? xDateTime; // For scatter charts, using DateTime for x
// final double y; // For column and other charts
// final String? timePeriod; // For line and bar charts
// final double? value; // For line and bar charts
// final double? y2; // For secondary data (stacked column chart)
// final double? y3; // For tertiary data (stacked column chart)
// final double? y4; // For quaternary data (stacked column chart)
//
// // Constructor that handles all chart cases
// ChartData({
// this.xInt,
// this.xDateTime,
// this.y = 0.0,
// this.timePeriod,
// this.value,
// this.y2,
// this.y3,
// this.y4,
// });
// }

View File

@ -22,11 +22,7 @@ import 'package:uae_stat/infrastructure/data/p_auth_repo.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/components/my_toggle.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/l10n/app_localizations.dart';
import '../components/my_bottom_nav_bar.dart';
import 'auth_verification/changepassword.dart';
import 'demo_home.dart';
class ProfileScreen extends ConsumerStatefulWidget {
final String userId; // Add this field to hold the user ID

View File

@ -26,13 +26,12 @@ import 'package:uae_stat/infrastructure/data/p_auth_repo.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/l10n/app_localizations.dart';
import 'package:uae_stat/presentation/Screens/demo_home.dart';
import 'package:uae_stat/presentation/components/dialogs.dart';
import 'package:uae_stat/presentation/components/lang_toggle.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_text_field.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import '../../Screens/auth_verification/registration.dart';
@ -896,7 +895,6 @@ class LoginRoute extends HookConsumerWidget {
final userId = session.id;
if (userId.isNotEmpty) {
final url =
// Uri.parse("https://pb.venbait.in/api/login_success?id=$userId");
Uri.parse('$apiUrl/api/login_success?id=$userId');
try {

View File

@ -12,7 +12,7 @@ import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home/home_route.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/components/constant/constant.dart';
import 'package:http/http.dart' as http;

View File

@ -1,165 +1,3 @@
// import 'dart:math';
//
// import 'package:external_repos/external_repos.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_hooks/flutter_hooks.dart';
// import 'package:hooks_riverpod/hooks_riverpod.dart';
// import 'package:uae_stat/config/my_theme.dart';
// import 'package:uae_stat/domain/use_cases/language.dart';
// import 'package:uae_stat/presentation/components/indicators/indicator_view.dart';
// import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
// import 'package:uae_stat/presentation/components/my_drawer.dart';
// import 'package:uae_stat/presentation/components/space.dart';
// import 'package:uae_stat/presentation/components/themed_app_bar.dart';
// import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
//
// bool _wasHomeNeverBuiltThisSession = true;
//
// class HomeRoute extends HookConsumerWidget {
// const HomeRoute({super.key});
//
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final bodyContent = Column(
// children: IndicatorTopic.values.map(
// (topic) {
// final scaledHeight = context.height * pow(50 / 880, 1.1);
// final titleText = Text(
// context.translate(
// topic.nameEN,
// topic.nameAR,
// ),
// textAlign: TextAlign.center,
// style: TextStyle(
// fontFamily: context.translate(
// 'Roboto',
// 'NotoKufi',
// ),
// color: Colors.white,
// fontSize: 20,
// fontWeight: FontWeight.w700,
// ),
// );
// final title = Container(
// height: context.height > 880 ? null : scaledHeight,
// width: context.width,
// margin: const EdgeInsets.symmetric(
// horizontal: 24,
// ),
// padding: const EdgeInsets.symmetric(
// vertical: 5,
// ),
// decoration: BoxDecoration(
// color: MyTheme.topicColor(topic),
// borderRadius: BorderRadius.circular(100),
// ),
// child: context.height > 880
// ? titleText
// : FittedBox(
// fit: BoxFit.scaleDown,
// child: titleText,
// ),
// );
// final ids = IndicatorEnum.values.where(
// (id) => id.topic == topic,
// );
// const horizontalGap = 16.0;
// const verticalGap = 9.0;
// final boxWidth = (context.width - (3 * horizontalGap)) / 2;
// final indicatorBoxes = ids
// .map(
// (id) => SizedBox(
// width: boxWidth,
// height: double.infinity,
// child: IndicatorView(id),
// ),
// )
// .toList();
// final scrollCtl = useScrollController();
// if (_wasHomeNeverBuiltThisSession) {
// WidgetsBinding.instance.addPostFrameCallback(
// (_) async {
// final duration = kThemeAnimationDuration * 0.75;
// const distance = -30.0;
// scrollCtl.animateTo(
// distance,
// duration: duration,
// curve: Curves.fastOutSlowIn,
// );
// await Future.delayed(duration * 2.5);
// scrollCtl.animateTo(
// distance,
// duration: duration,
// curve: Curves.fastOutSlowIn,
// );
// _wasHomeNeverBuiltThisSession = false;
// },
// );
// }
// final content = SingleChildScrollView(
// controller: scrollCtl,
// padding: const EdgeInsets.symmetric(
// vertical: verticalGap,
// horizontal: horizontalGap,
// ),
// scrollDirection: Axis.horizontal,
// child: Column(
// children: [
// Expanded(
// child: Row(
// children: [
// indicatorBoxes[0],
// horizontalGap.horizontalSpace,
// indicatorBoxes[1],
// horizontalGap.horizontalSpace,
// indicatorBoxes[2],
// ],
// ),
// ),
// verticalGap.verticalSpace,
// Expanded(
// child: Row(
// children: [
// indicatorBoxes[3],
// horizontalGap.horizontalSpace,
// indicatorBoxes[4],
// horizontalGap.horizontalSpace,
// indicatorBoxes.length == 6
// ? indicatorBoxes[5]
// : boxWidth.horizontalSpace,
// ],
// ),
// ),
// ],
// ),
// );
// return Expanded(
// child: Column(
// children: [
// title,
// Expanded(
// child: content,
// ),
// ],
// ),
// );
// },
// ).toList(),
// );
// return SafeArea(
// bottom: false,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: [
// const ThemedAppBar(),
// Expanded(
// child: bodyContent,
// ),
// ],
// ),
// );
// }
// }
import 'dart:convert';
// import 'dart:io';

View File

@ -1,24 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
class FootNoteText extends ConsumerWidget {
const FootNoteText(this.text, {super.key});
final String text;
@override
Widget build(BuildContext context, WidgetRef ref) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 11),
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontFamily: context.translate('Roboto', 'NotoKufi'),
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
);
}

View File

@ -1,87 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/domain/use_cases/preferences_use_case.dart';
import 'package:uae_stat/presentation/components/dialogs.dart';
class IndicatorFavoriteButton extends ConsumerWidget {
const IndicatorFavoriteButton(this.ie, {super.key});
final IndicatorEnum ie;
@override
Widget build(BuildContext context, WidgetRef ref) {
final icon = ref
.watch(
preferencesUseCaseProvider.select(
(asyncPrefs) => asyncPrefs.whenData(
(prefs) => prefs.favoriteIndicators.contains(ie),
),
),
)
.when(
data: (isFav) => isFav ? Icons.favorite : Icons.favorite_border,
error: Error.throwWithStackTrace,
loading: () => Icons.hourglass_empty,
);
return IconButton(
padding: EdgeInsets.zero,
iconSize: 20,
color: Colors.white,
icon: Icon(icon),
onPressed: () async {
final prefs = await ref.read(
preferencesUseCaseProvider.future,
);
final favs = prefs.favoriteIndicators;
if (!context.mounted) return;
final wasFavorite = favs.contains(ie);
if (wasFavorite) {
final wantsToDelete = await context.confirmationDialog(
context.translate(
'Confirm removal',
'تأكيد الإزالة',
),
context.translate(
'Are you sure you want to remove ${ie.titleEN} from favorites? You will no longer receive notifications regarding this item.',
'هل أنت متأكد من أنك تريد إزالة ${ie.titleAR} من المفضلة؟ لن تتلقى بعد الآن إشعارات بخصوص هذا العنصر.',
),
);
if (wantsToDelete == false || wantsToDelete == null) return;
if (!context.mounted) return;
return context.loaderWithErrorDialog(
() => ref
.read(
preferencesUseCaseProvider.notifier,
)
.updatePreferences(
(prefs) => prefs.copyWith(
favoriteIndicators: prefs.favoriteIndicators.remove(
ie,
),
),
),
);
} else {
await context.loaderWithErrorDialog(
() => ref
.read(
preferencesUseCaseProvider.notifier,
)
.updatePreferences(
(prefs) => prefs.copyWith(
favoriteIndicators: prefs.favoriteIndicators.add(
ie,
),
),
),
);
if (!context.mounted) return;
context.go('/${context.language}/my-favorites');
}
},
);
}
}

View File

@ -1,274 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/config/old_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/infrastructure/services/packages/widget_to_img.dart';
import 'package:uae_stat/presentation/components/space.dart';
class SpecificDetailBoxView extends ConsumerWidget {
const SpecificDetailBoxView(
this.data, {
this.iconPath = IndicatorIconAssetPath.bigHospital,
required this.title,
this.footnote,
super.key,
});
final String? footnote;
final String iconPath;
final String title;
final SpecificDetailBoxData data;
@override
Widget build(BuildContext context, WidgetRef ref) {
final indicatorName = IndicatorEnum.fromJson(
GoRouterState.of(context).path!,
);
final indicatorTopic = indicatorName.topic;
final foregroundColor = MyTheme.topicColor(indicatorTopic);
final latest = Column(
children: [
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
if (data.title2 != null) ...[
Text(
context.translateFromTuple(data.title2!),
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 11,
fontWeight: FontWeight.w800,
color: foregroundColor.shade700,
),
),
4.verticalSpace,
],
Text(
context.translateFromTuple(data.subtitle),
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily:
// ref.watch(languageProvider) == Language.ar
// ? 'NotoKufi'
// :
'Roboto',
fontSize: 11,
fontWeight: FontWeight.w400,
color: Color(0xff898C81),
),
),
Text(
_formatValue(
data.value,
isPercentage: data.isValuePercentage,
),
textAlign: TextAlign.center,
style: TextStyle(
fontFamily:
// ref.watch(languageProvider) == Language.ar
// ? 'NotoKufi'
// :
'Roboto',
fontSize: 26,
fontWeight: FontWeight.w800,
color: foregroundColor,
),
),
],
);
final prev = data.prevValue == null
? null
: Column(
children: [
2.verticalSpace,
const SizedBox(
width: 80,
child: Divider(
height: 0,
),
),
4.verticalSpace,
Stack(
alignment: Alignment.center,
children: [
if (data.didImprove == Improvement.positive)
const Padding(
padding: EdgeInsetsDirectional.only(
start: 72,
),
child: Icon(
CupertinoIcons.up_arrow,
color: Colors.green,
size: 16,
),
),
Text(
_formatValue(
data.prevValue!,
isPercentage: data.isPrevValuePercentage ?? false,
),
style: TextStyle(
fontFamily:
// ref.watch(languageProvider) == Language.ar
// ? 'NotoKufi'
// :
'Roboto',
fontWeight: FontWeight.w700,
fontSize: 16,
color: data.didImprove == null
? null
: switch (data.didImprove!) {
Improvement.positive => Colors.green,
Improvement.negative => Colors.red,
Improvement.none => Colors.red,
},
),
),
],
),
Text(
context.translateFromTuple(data.prevValueSubtitle!),
style: const TextStyle(
fontFamily:
// ref.watch(languageProvider) == Language.ar
// ? 'NotoKufi'
// :
'Roboto',
fontWeight: FontWeight.w500,
fontSize: 8,
color: Colors.grey,
),
),
],
);
final icon = Image.asset(
iconPath,
height: 28,
color: foregroundColor,
);
final bodyContent = Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
icon,
8.verticalSpace,
latest,
if (prev != null) prev,
if (footnote != null)
Text(
footnote!,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 8,
color: Colors.grey.shade700,
),
),
],
);
final cardBg = Screenshottable(
child: DecoratedBox(
decoration: OldTheme.cardDecoration,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
child: bodyContent,
),
),
);
final button = InkWell(
onTap: () => showDialog(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (dialogCtx) {
final shareBtn = ElevatedButton.icon(
onPressed: () async {
final imgBytes = await cardBg.generateScreenshot();
final xFile = XFile.fromData(
imgBytes,
mimeType: 'image/png',
name: '$title.png',
);
Share.shareXFiles(
[xFile],
subject: title,
);
},
label: Text(
context.translate(
'Share',
'يشارك',
),
),
icon: const Icon(Icons.share),
);
final interactiveViewer = InteractiveViewer(
boundaryMargin: const EdgeInsets.all(double.infinity),
alignment: Alignment.center,
minScale: 1,
child: FractionallySizedBox(
heightFactor: 1 / 3,
child: FittedBox(child: cardBg),
),
);
return InkWell(
onTap: dialogCtx.pop,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 24,
),
child: Row(
children: [
shareBtn,
],
),
),
Expanded(
child: interactiveViewer,
),
],
),
);
},
),
child: cardBg,
);
return button;
}
String _formatValue(
num n, {
required bool isPercentage,
}) =>
'${n > 9999999 ? n.toAbbreviatedString() : n.toStringWithCommas(
hideDecimals: n > 99999,
)}${isPercentage ? "%" : ""}';
}

View File

@ -1,192 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/domain/use_cases/stat_data/indicator_use_case.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/ref_watch_handled.dart';
import 'package:uae_stat/presentation/components/slide_up_sliver_delegate.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/indicator_favorite_button.dart';
class UaeNumbersScaffold<T extends IndicatorEntity> extends ConsumerWidget {
const UaeNumbersScaffold({
required this.builder,
this.appBarActions = const [],
super.key,
});
final Widget Function(T entity) builder;
final Iterable<Widget> appBarActions;
IndicatorEnum _ieFromPath(BuildContext context) {
final path = GoRouterState.of(context).path!;
return IndicatorEnum.fromJson(path);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final ie = _ieFromPath(context);
final provider = IndicatorProvider(ie);
final topicColor = MyTheme.topicColor(ie.topic);
final themedAppBar = ColoredBox(
color: topicColor,
child: ThemedAppBar(
titleText: context.translate(
BottomNavBarItem.uaeNumbers.titleEN,
BottomNavBarItem.uaeNumbers.titleAR,
),
foreGroundColor: Colors.white,
),
);
final bodyContent = CustomScrollView(
slivers: [
SliverPersistentHeader(
delegate: SlideUpSliverDelegate(
child: _AppBar(
ie,
actions: appBarActions,
),
maxExtent: context.translate(143.5, 166.5),
minExtent: context.translate(143.5, 166.5),
),
pinned: true,
floating: true,
),
SliverToBoxAdapter(
child: RefWatchHandled(
provider,
onData: (data) => SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(25, 25, 25, 25),
child: builder(data as T),
),
),
),
],
);
final body = Column(
children: [
ColoredBox(
color: topicColor,
child: const SizedBox(
height: kToolbarHeight,
width: double.infinity,
),
),
themedAppBar,
Expanded(
child: bodyContent,
),
],
);
final bgBody = ColoredBox(
color: MyTheme.topicColor(ie.topic).shade100,
child: body,
);
return bgBody;
}
}
class _AppBar extends ConsumerWidget {
const _AppBar(
this.ie, {
this.actions = const [],
});
final IndicatorEnum ie;
final Iterable<Widget> actions;
@override
Widget build(BuildContext context, ref) {
final topicHeading = Text(
context
.translate(
ie.topic.nameEN,
ie.topic.nameAR,
)
.toUpperCase(),
textAlign: TextAlign.start,
style: TextStyle(
color: Colors.white,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontWeight: FontWeight.w600,
fontSize: 26,
),
);
final indicatorSubheading = Text(
context.translate(
ie.titleEN,
ie.titleAR,
),
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontWeight: FontWeight.w500,
fontSize: 18,
color: Colors.white,
),
);
final textColumn = Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
topicHeading,
const SizedBox(
width: 50,
child: Divider(
color: Colors.white,
thickness: 4,
),
),
FittedBox(child: indicatorSubheading),
],
);
// // TODO enable share
// final shareBtn = IconButton(
// padding: EdgeInsets.zero,
// iconSize: 20,
// color: Colors.white,
// icon: const Icon(CupertinoIcons.arrowshape_turn_up_right),
// onPressed: () {},
// );
final content = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
9.5.verticalSpace,
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25,
),
child: textColumn,
),
9.5.verticalSpace,
Align(
alignment: AlignmentDirectional.topEnd,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
...actions,
IndicatorFavoriteButton(ie),
12.horizontalSpace,
// shareBtn,
],
),
),
],
);
final coloredBox = ColoredBox(
color: MyTheme.topicColor(ie.topic),
child: content,
);
return coloredBox;
}
}

View File

@ -1,425 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/config/old_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/components/indicators/indicator_view.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
class ListUAENumbersRoute extends HookConsumerWidget {
const ListUAENumbersRoute({super.key});
@override
Widget build(
BuildContext context,
WidgetRef ref,
) {
final expandedTopic = useState<IndicatorTopic?>(
IndicatorTopic.economy,
);
final scrollCtl = useScrollController();
final searchCtl = useTextEditingController();
final bodyContentSliver = ValueListenableBuilder(
valueListenable: searchCtl,
builder: (context, value, _) {
final cleanedSearchTerm = value.text
.replaceAll(' ', '')
.replaceAll(
RegExp(
r'[^\w\s]',
multiLine: true,
caseSensitive: false,
),
'',
)
.toLowerCase();
return ListView.builder(
padding: const EdgeInsets.only(
left: 25,
right: 25,
bottom: 125,
),
itemCount: IndicatorTopic.values.length,
itemBuilder: (context, i) => _PerTopicSeriesView(
IndicatorTopic.values[i],
cleanedSearchTerm: cleanedSearchTerm,
isExpanded: IndicatorTopic.values[i] == expandedTopic.value,
onTap: () => expandedTopic.value == IndicatorTopic.values[i]
? expandedTopic.value = null
: expandedTopic.value = IndicatorTopic.values[i],
),
);
},
);
final searchBar = TextField(
controller: searchCtl,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderSide: const BorderSide(
color: OldTheme.primary,
),
borderRadius: BorderRadius.circular(100),
),
prefixIcon: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(CupertinoIcons.search),
),
suffixIcon: ValueListenableBuilder(
valueListenable: searchCtl,
builder: (context, value, child) => value.text.isEmpty
? const SizedBox.shrink()
: IconButton(
onPressed: searchCtl.clear,
icon: const Icon(Icons.clear),
),
),
prefixIconConstraints: const BoxConstraints(minWidth: 50),
hintText: context.translate(
'Search',
'ابحث عن أرقام الإمارات',
),
constraints: const BoxConstraints(),
contentPadding: const EdgeInsetsDirectional.only(),
),
);
final animatedSearchBar = SliverAppBar(
snap: true,
leading: const SizedBox.shrink(),
floating: true,
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
backgroundColor: Colors.transparent,
actions: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 25,
),
child: searchBar,
),
),
],
);
final customScrollView = CustomScrollView(
controller: scrollCtl,
slivers: [
animatedSearchBar,
SliverFillRemaining(child: bodyContentSliver),
],
);
final body = Column(
children: [
ThemedAppBar(
titleText: context.translate(
BottomNavBarItem.uaeNumbers.titleEN,
BottomNavBarItem.uaeNumbers.titleAR,
),
),
Expanded(child: customScrollView),
],
);
final bgAdded = SafeArea(
bottom: false,
child: ColoredBox(
color: Colors.white,
child: body,
),
);
return bgAdded;
}
}
class _PerTopicSeriesView extends HookConsumerWidget {
const _PerTopicSeriesView(
this.topic, {
required this.cleanedSearchTerm,
required this.isExpanded,
required this.onTap,
});
final IndicatorTopic topic;
final String cleanedSearchTerm;
final bool isExpanded;
final void Function() onTap;
static const _divisions = {
IndicatorTopic.economy: {
(en: 'National Accounts', ar: 'الحسابات القومية'): {
IndicatorEnum.gdpConstant,
},
(en: 'International Trade', ar: 'التجارة الدولية'): {
IndicatorEnum.tradeValue,
},
(en: 'Prices', ar: 'الأسعار'): {IndicatorEnum.inflationRate},
(en: 'Tourism', ar: 'السياحة'): {IndicatorEnum.hotelGuests},
(en: 'Air Transport', ar: 'النقل الجوي'): {
IndicatorEnum.aircraftMovement,
},
},
IndicatorTopic.social: {
(en: 'Population', ar: 'السكان'): {IndicatorEnum.population},
(en: 'Vital Statistics', ar: 'الإحصاءات الحيوية'): <IndicatorEnum>{},
(en: 'Education', ar: 'التعليم'): {
IndicatorEnum.studentsGeneral,
IndicatorEnum.studentsHigher,
},
(en: 'Health', ar: 'الصحة'): {
IndicatorEnum.hospitalsGovernment,
IndicatorEnum.hospitalsPrivate,
},
(en: 'Labor Force', ar: 'القوى العاملة'): {
IndicatorEnum.laborForce,
},
},
IndicatorTopic.environment: {
(en: 'Agriculture', ar: 'الزراعة'): <IndicatorEnum>{},
(en: 'Environment', ar: 'البيئة'): {
IndicatorEnum.desalinatedWaterProduction,
IndicatorEnum.nationalReservesArea,
},
(en: 'Energy', ar: 'الطاقة'): {
IndicatorEnum.electricityProduction,
},
(en: 'Health', ar: 'الصحة'): {
IndicatorEnum.aircraftMovement,
},
},
};
@override
Widget build(BuildContext context, WidgetRef ref) {
final topicText = Text(
context.translate(
topic.nameEN.toUpperCase(),
topic.nameAR,
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 24,
fontWeight: FontWeight.w600,
color: Colors.white,
),
);
final topicBar = Container(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 18,
),
decoration: BoxDecoration(
color: MyTheme.topicColor(topic),
borderRadius: BorderRadius.circular(100),
),
constraints: const BoxConstraints(
// maxWidth: topicBarWidth,
),
child: Row(
children: [
Expanded(child: topicText),
6.horizontalSpace,
AnimatedRotation(
duration: kThemeAnimationDuration,
turns: isExpanded ? 0 : 0.5,
child: const Icon(
CupertinoIcons.chevron_up,
color: Colors.white,
),
),
],
),
);
final topicBtn = InkWell(
onTap: onTap,
child: topicBar,
);
// const horizontalGap = 16.0;
// const verticalGap = 9.0;
// final boxWidth = (context.width - (2 * 25) - horizontalGap) / 2;
// final indViewList = _divisions[topic]!
// .entries
// .map<Widget>(
// (e) => SizedBox(
// width: boxWidth,
// height: 80,
// child: IndicatorView(e),
// ),
// )
// .toList();
// if (indViewList.length == 1) {
// indViewList.add(400.horizontalSpace);
// }
// if (indViewList.isEmpty) {
// indViewList.add(
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 80),
// child: SizedBox(
// width: double.infinity,
// child: Text(
// 'Empty',
// textAlign: TextAlign.center,
// ),
// ),
// ),
// );
// }
// final animatedSeriesCards = AnimatedSize(
// duration: kThemeAnimationDuration,
// alignment: Alignment.bottomCenter,
// child: isExpanded
// ? Padding(
// padding: const EdgeInsets.only(
// top: verticalGap + 3,
// bottom: 3,
// ),
// child: Wrap(
// spacing: horizontalGap,
// runSpacing: verticalGap,
// crossAxisAlignment: WrapCrossAlignment.start,
// children: indViewList,
// ),
// )
// : double.infinity.horizontalSpace,
// );
return Column(
children: [
topicBtn,
Column(
children: _divisions[topic]!.entries.map(
(e) {
final subtopic = e.key;
final ids = e.value;
return _PerSubtopicBody(
cleanedSearchTerm: cleanedSearchTerm,
ids: ids,
topic: topic,
titleText: subtopic,
);
},
).toList(),
),
25.verticalSpace,
],
);
}
}
class _PerSubtopicBody extends ConsumerWidget {
const _PerSubtopicBody({
required this.cleanedSearchTerm,
required this.ids,
required this.topic,
required this.titleText,
});
final String cleanedSearchTerm;
final Translatable titleText;
final IndicatorTopic topic;
final Set<IndicatorEnum> ids;
Color get _titleColor => switch (topic) {
IndicatorTopic.economy => const Color(0xff6C8BAF),
IndicatorTopic.social => MyTheme.topicColor(topic).shade200,
IndicatorTopic.environment => MyTheme.topicColor(topic).shade400,
};
int _lastRowFirstI(int maxBoxesInOneRow, int nBoxes) {
final remainder = nBoxes % maxBoxesInOneRow;
return nBoxes - remainder;
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final title = Text(
context.translateFromTuple(
titleText,
),
style: TextStyle(
color: _titleColor,
fontWeight: FontWeight.w600,
fontSize: 16,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
),
);
final shownIds = ids.where(
(e) => e.matchSearch(cleanedSearchTerm),
);
const horizontalGap = 16.0;
const verticalGap = 9.0;
final boxWidth = (context.width - (2 * 25) - horizontalGap) / 2;
final indViewList = shownIds
.map(
(e) => SizedBox(
width: boxWidth,
height: 80,
child: IndicatorView(e),
),
)
.toList();
final availableWidth = context.width - 50;
final maxBoxesInOneRow = (availableWidth / boxWidth).floor();
final lastRowFirstI = _lastRowFirstI(
maxBoxesInOneRow,
shownIds.length,
);
final lastRow = Row(
children: indViewList
.sublist(
lastRowFirstI,
shownIds.length,
)
.map((e) => Expanded(child: e))
.toList(),
);
final animatedSeriesCards = AnimatedSize(
duration: kThemeAnimationDuration,
alignment: Alignment.bottomCenter,
child: indViewList.isEmpty
? double.infinity.horizontalSpace
: Padding(
padding: const EdgeInsets.only(
top: verticalGap + 3,
bottom: 3,
),
child: Wrap(
spacing: horizontalGap,
runSpacing: verticalGap,
crossAxisAlignment: WrapCrossAlignment.start,
children: [
...indViewList.sublist(0, lastRowFirstI),
lastRow,
],
),
),
);
const comingSoon = Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: SizedBox(
width: double.infinity,
child: Text(
'Coming soon',
textAlign: TextAlign.center,
),
),
);
return Column(
children: [
12.verticalSpace,
title,
ids.isEmpty ? comingSoon : animatedSeriesCards,
],
);
}
}

View File

@ -1,346 +0,0 @@
import 'dart:math';
import 'package:external_repos/external_repos.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/list.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class AircraftBody extends ConsumerWidget {
const AircraftBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<AircraftsEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.totalAircraftMovement,
title: context.translate(
'Aircrafts Movement',
'حركة الطائرات',
),
iconPath: IndicatorIconAssetPath.aircraftMovement,
),
),
18.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.arrivals,
title: context.translate(
'Arrivals',
'الوصول',
),
iconPath: IndicatorIconAssetPath.arrivals,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.departures,
title: context.translate(
'Departures',
'المغادرة',
),
iconPath: IndicatorIconAssetPath.departures,
),
),
],
),
],
);
final weeklyFlightsChart = GraphHolderCard(
titleText: context.translate(
'Annual Trend',
'الاتجاه السنوي',
),
_AnnualTrendChart(
titles: data.weeklyFlightsGraphData.keys,
data: data.weeklyFlightsGraphData.map(
(key, value) => MapEntry(
int.parse(key.en),
[
value.fromUAE,
value.toUAE,
],
),
),
),
// ThemedLineChart(
// lineDataList: [
// LineChartDataEntity(
// title: context.translate(
// 'Flights from UAE',
// 'رحلات جوية من الإمارات العربية المتحدة',
// ),
// data: data.weeklyFlightsGraphData.map(
// (name, val) => MapEntry(
// double.parse(name.en),
// val.fromUAE.toDouble(),
// ),
// ),
// color: MyTheme.topicColor(data.id.topic).shade700,
// ),
// LineChartDataEntity(
// title: context.translate(
// 'Flights to UAE',
// 'رحلات جوية إلى الإمارات العربية المتحدة',
// ),
// data: data.weeklyFlightsGraphData.map(
// (name, val) => MapEntry(
// double.parse(name.en),
// val.toUAE.toDouble(),
// ),
// ),
// color: MyTheme.topicColor(data.id.topic).shade50,
// ),
// ],
// yAxisTitleBuilder: (value) => value.toInt().toString(),
// ),
);
final topDestinationsGraph = data.topDestinations == null
? const Placeholder()
: GraphHolderCard(
HorizontalBarChart(
data.topDestinations!.data.map(
(name, val) => MapEntry(
context.translate(name.en, name.ar),
(
value: val.toDouble(),
barEndText: val.toStringWithCommas(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Top Destinations',
'أفضل الوجهات',
),
);
final travelTypeDistributionGraph = GraphHolderCard(
ThemedPieChartSingular(
materialColor: MyTheme.topicColor(data.id.topic),
data: {
context.translate(
'Departures',
'المغادرة',
): data.departures.value.toDouble(),
context.translate(
'Arrivals',
'الوصول',
): data.arrivals.value.toDouble(),
},
),
titleText: context.translate(
'Travel Type Distribution',
'توزيع نوع السفر',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
travelTypeDistributionGraph,
if (data.topDestinations != null) ...[
18.verticalSpace,
topDestinationsGraph,
],
18.verticalSpace,
weeklyFlightsChart,
18.verticalSpace,
],
);
},
);
}
class _AnnualTrendChart extends ConsumerWidget {
const _AnnualTrendChart({
required this.data,
required this.titles,
});
final Map<num, Iterable<num>> data;
final Iterable<Translatable> titles;
@override
Widget build(BuildContext context, ref) {
final axisVals = Row(
children: data.keys
.map(
(e) => Expanded(
child: Transform.rotate(
angle: pi * -0.2,
child: Align(
alignment: Alignment.center,
child: Text(
e.toString(),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 11,
color: Colors.black,
),
),
),
),
),
)
.toList(),
);
final maxHeight = _caclulateMaxHeight();
final topColor = MyTheme.topicColor(IndicatorTopic.economy).shade50;
final bottomColor = MyTheme.topicColor(IndicatorTopic.economy).shade400;
final graph = LayoutBuilder(
builder: (context, constraints) {
final totalHeight = constraints.maxHeight;
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: data.values.map(
(e) {
final top = Container(
color: topColor,
width: 20,
height: totalHeight * (e.first / maxHeight),
alignment: Alignment.center,
child: RotatedBox(
quarterTurns: -1,
child: Text(
e.first.toStringWithCommas(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 11,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontWeight: FontWeight.bold,
),
),
),
);
final bottom = Container(
color: bottomColor,
width: 20,
height: totalHeight * (e.last / maxHeight),
alignment: Alignment.center,
child: RotatedBox(
quarterTurns: -1,
child: Text(
e.last.toStringWithCommas(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 11,
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontWeight: FontWeight.bold,
),
),
),
);
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
top,
bottom,
],
),
);
},
).toList(),
);
},
);
final legend = Column(
children: [
Row(
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color: topColor,
borderRadius: BorderRadius.circular(2),
),
height: 8,
width: 8,
),
3.horizontalSpace,
Text(
context.translateFromTuple(titles.first),
style: const TextStyle(
fontSize: 11,
),
),
],
),
14.horizontalSpace,
Row(
children: [
Container(
decoration: BoxDecoration(
color: bottomColor,
borderRadius: BorderRadius.circular(2),
),
height: 8,
width: 8,
),
3.horizontalSpace,
Text(
context.translateFromTuple(titles.last),
style: const TextStyle(
fontSize: 11,
),
),
],
),
],
),
]..intersperseDivider(2.verticalSpace),
);
return Column(
children: [
legend,
Expanded(child: graph),
12.verticalSpace,
axisVals,
],
);
}
num _caclulateMaxHeight() {
return data.values.fold(
0,
(prevMax, spots) {
final localMaxima = spots.sumBy(
(e) => e.toInt(),
);
if (localMaxima > prevMax) return localMaxima;
return prevMax;
},
);
}
}

View File

@ -1,322 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/my_toggle.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
enum _GdpVersion {
constant(
nameEN: 'Constant',
nameAR: 'ثابت',
),
current(
nameEN: 'Current',
nameAR: 'الحالي',
);
const _GdpVersion({required this.nameEN, required this.nameAR});
factory _GdpVersion.fromBool(bool b) => switch (b) {
true => _GdpVersion.constant,
false => _GdpVersion.current,
};
final String nameEN;
final String nameAR;
bool toBool() => switch (this) {
_GdpVersion.constant => true,
_GdpVersion.current => false,
};
}
class GdpBody extends HookConsumerWidget {
const GdpBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final gdpVersion = useState<_GdpVersion>(
_GdpVersion.constant,
);
return UaeNumbersScaffold<GdpEntity>(
appBarActions: [
ValueListenableBuilder(
valueListenable: gdpVersion,
builder: (context, value, child) => _VersionToggle(
version: value,
onTap: () => gdpVersion.value = _GdpVersion.fromBool(
!value.toBool(),
),
),
),
],
builder: (entity) {
final data = switch (gdpVersion.value) {
_GdpVersion.constant => entity.constant,
_GdpVersion.current => entity.current,
};
final boxesGrid = Column(
children: [
IntrinsicHeight(
child: Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.gdp,
iconPath: IndicatorIconAssetPath.gdp,
title: context.translate(
'GDP',
'الناتج المحلي الإجمالي',
),
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
iconPath: IndicatorIconAssetPath.growth,
data.gdpGrowthRate,
title: context.translate(
'Growth Rate',
'معدل النمو',
),
),
),
],
),
),
18.verticalSpace,
IntrinsicHeight(
child: Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.nonOilGdp,
iconPath: IndicatorIconAssetPath.gdp,
title: context.translate(
'Non-oil GDP',
'الناتج المحلي الإجمالي غير النفطي',
),
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.nonOilGdpGrowthRate,
iconPath: IndicatorIconAssetPath.growth,
title: context.translate(
'Growth Rate',
'معدل النمو',
),
),
),
],
),
),
],
);
final econActByGrowthRateGraphData =
data.topEconomicActivitiesByGrowthRate.data.map(
(name, val) => MapEntry(
name,
(
value: val,
barEndText: '${val.toAbbreviatedString()}%',
),
),
);
final economicActivitiesByGrowthRateGraph = GraphHolderCard(
HorizontalBarChart(
econActByGrowthRateGraphData.map(
(key, value) => MapEntry(
context.translateFromTuple(key),
value,
),
),
shortlistTopN: 5,
color: MyTheme.topicColor(IndicatorTopic.economy),
),
titleText: context.translate(
'Top Economic Activities by Growth Rate',
'الأنشطة الاقتصادية الأعلى نموا',
),
subtitleText: context.translate(
'(${data.topEconomicActivitiesByGrowthRate.year}) (AED)',
'(${data.topEconomicActivitiesByGrowthRate.year}) (درهم)',
),
overrideHorizontalPadding: 12,
);
final gdpValueGraph = GraphHolderCard(
ThemedLineChart(
yAxisTitleBuilder: (x) => x.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: context.translate(
'GDP',
'الناتج المحلي الإجمالي',
),
data: data.gdpValueGraphData.map(
(name, val) => MapEntry(
name.toDouble(),
val.gdp.toDouble(),
),
),
color: MyTheme.topicColor(entity.id.topic).shade100,
),
LineChartDataEntity(
title: context.translate(
'Non-oil GDP',
'الناتج المحلي الإجمالي غير النفطي',
),
data: data.gdpValueGraphData.map(
(name, val) => MapEntry(
name.toDouble(),
val.nonOilGdp.toDouble(),
),
),
color: MyTheme.topicColor(entity.id.topic).shade900,
),
],
),
titleText: context.translate(
'GDP Value',
'اتجاه الناتج المحلي الإجمالي',
),
subtitleText: context.translate('AED', 'درهم'),
);
final topEconomicActivitiesByContributionGraph = GraphHolderCard(
HorizontalBarChart(
shortlistTopN: 5,
data.topEconomicActivitiesByContribution.data.map(
(name, val) => MapEntry(
context.translateFromTuple(name),
(
value: val,
barEndText: '${val.toAbbreviatedString()}%',
),
),
),
color: MyTheme.topicColor(IndicatorTopic.economy),
),
titleText: context.translate(
'Top Economic Activities by Contribution',
'الأنشطة الاقتصادية الأعلى مساهمة',
),
subtitleText:
'(${data.topEconomicActivitiesByContribution.year}) (${context.translate(
'AED',
'درهم',
)})',
overrideHorizontalPadding: 12,
);
final footNoteText = FootNoteText(
context.translate(
'*2022 Data are sourced from the Economic Survey. **2023 data are Preliminary Estimation',
'*بيانات 2022 مصدرها المسح الاقتصادي. **بيانات 2023 هي تقديرات أولية.',
),
);
return Column(
children: [
boxesGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
economicActivitiesByGrowthRateGraph,
18.verticalSpace,
gdpValueGraph,
18.verticalSpace,
topEconomicActivitiesByContributionGraph,
],
);
},
);
}
}
class _VersionToggle extends HookConsumerWidget {
const _VersionToggle({
required this.version,
required this.onTap,
});
final _GdpVersion version;
final void Function() onTap;
@override
Widget build(BuildContext context, WidgetRef ref) {
final textStyle = TextStyle(
color: Colors.white,
fontSize: 12,
fontFamily: context.translate('Roboto', 'NotoKufi'),
);
final animCtl = useAnimationController(
duration: kThemeAnimationDuration,
initialValue: version.toBool() ? 1 : 0,
);
version.toBool() ? animCtl.forward() : animCtl.reverse();
final styleTween = TextStyleTween(
begin: textStyle.copyWith(
color: textStyle.color?.withOpacity(0.6),
fontWeight: FontWeight.w400,
),
end: textStyle.copyWith(
fontWeight: FontWeight.w600,
),
);
final anim = CurvedAnimation(
parent: animCtl,
curve: Curves.elasticInOut,
);
final content = Row(
children: [
DefaultTextStyleTransition(
style: styleTween.animate(anim),
child: Text(
context.translate(
_GdpVersion.constant.nameEN,
_GdpVersion.constant.nameAR,
),
),
),
// MyToggle(
// isOn: version.toBool(),
// onTap: null,
//
// ),
DefaultTextStyleTransition(
style: styleTween.animate(
Animation.fromValueListenable(
anim,
transformer: (val) => 1 - val,
),
),
child: Text(
context.translate(
_GdpVersion.current.nameEN,
_GdpVersion.current.nameAR,
),
),
),
],
);
final btn = GestureDetector(
onTap: onTap,
child: Material(
color: Colors.transparent,
child: content,
),
);
return btn;
}
}

View File

@ -1,164 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_vertical_bar_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class HotelsBody extends ConsumerWidget {
const HotelsBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<HotelsEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.occupancyRate,
title: context.translate(
'Occupancy Rate',
'معدل الإشغال',
),
iconPath: IndicatorIconAssetPath.occupancyRate,
),
),
18.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.averageDailyRate,
title: context.translate(
'Average Daily Rate (ADR)',
'متوسط ​​السعر اليومي (ADR)',
),
iconPath: IndicatorIconAssetPath.averageDailyRate,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.averageLengthOfStay,
title: context.translate(
'Average Length of Stay',
'متوسط ​​مدة الإقامة',
),
iconPath: IndicatorIconAssetPath.averageLengthOfStay,
),
),
],
),
],
);
final guestNightsGraph = GraphHolderCard(
subtitleText: data.guestNights.year.toString(),
HorizontalBarChart(
data.guestNights.data.map(
(origin, qty) => MapEntry(
context.translateFromTuple(origin),
(
value: qty.toDouble(),
barEndText: qty.toAbbreviatedString(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Guest Nights',
'ليالي الضيوف',
),
);
final guestArrivalGraph = GraphHolderCard(
subtitleText: data.guestArrival.year.toString(),
HorizontalBarChart(
data.guestArrival.data.map(
(origin, qty) => MapEntry(
context.translateFromTuple(origin),
(
value: qty.toDouble(),
barEndText: qty.toAbbreviatedString(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Guest Arrival',
'وصول الضيف',
),
);
final revenueGraph = GraphHolderCard(
titleText: context.translate(
'Revenue',
'ربح',
),
subtitleText: data.revenue.year.toString(),
overrideHorizontalPadding: 0,
SizedBox(
width: double.infinity,
child: SingleChildScrollView(
child: ThemedPieChartPlural(
materialColor: MyTheme.topicColor(data.id.topic),
data: data.revenue.data.map(
(name, qty) => MapEntry(
context.translateFromTuple(name),
qty.toDouble(),
),
),
),
),
),
);
final hotelAndHotelApartmentsGraph =
data.hotelAndHotelApartmentsGraphData == null
? null
: GraphHolderCard(
ThemedVerticalBarChart(
data.hotelAndHotelApartmentsGraphData!.map(
(year, bars) => MapEntry(
year.toString(),
bars.map(
(key, value) => MapEntry(
context.translateFromTuple(key),
value,
),
),
),
),
materialColor: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Hotel and Hotel Apartments',
'الفنادق والشقق الفندقية',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
guestArrivalGraph,
18.verticalSpace,
guestNightsGraph,
18.verticalSpace,
revenueGraph,
if (hotelAndHotelApartmentsGraph != null) ...[
18.verticalSpace,
hotelAndHotelApartmentsGraph,
],
],
);
},
);
}

View File

@ -1,165 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:sdmx/sdmx.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class InflationBody extends ConsumerWidget {
const InflationBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<InflationEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.inflationRate,
iconPath: IndicatorIconAssetPath.inflationRate,
title: context.translate(
'Inflation Rate',
'معدل التضخم',
),
),
),
18.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.highestMainGroup,
iconPath: IndicatorIconAssetPath.highestMainGroup,
title: context.translate(
'Highest Main Group',
'المجموعة الاعلى تضخم',
),
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.lowestMainGroup,
iconPath: IndicatorIconAssetPath.lowestMainGroup,
title: context.translate(
'Lowest Main Group',
'المجموعة الأقل تضخم',
),
),
),
],
),
],
);
final inflationByMajorGroupsChart = GraphHolderCard(
subtitleText: data.inflationByMajorGroups.year.toString(),
titleText: context.translate(
'Inflation By Major Groups',
'التضخم حسب المجموعات الرئيسية',
),
graphHeightDelta: 100,
HorizontalBarChart(
data.inflationByMajorGroups.data.map(
(name, val) => MapEntry(
context.translateFromTuple(name),
(
barEndText: '${val.toStringAsFixed(2)}%',
value: val,
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
);
final inflationTimeSeriesChart = GraphHolderCard(
titleText: context.translate(
'Inflation Time Series',
'سلسلة زمنية للتضخم',
),
subtitleText: data.inflationTimeSeries.year.toString(),
ThemedLineChart(
yAxisInterval: 0.25,
yAxisTitleBuilder: (yearDotQuarter) {
final quarter =
switch (yearDotQuarter - yearDotQuarter.truncate()) {
0.25 => 1,
0.5 => 2,
0.75 => 3,
0 => 4,
_ => throw yearDotQuarter,
};
final year = quarter < 4
? yearDotQuarter.truncate()
: yearDotQuarter.truncate() - 1;
final qtp = QuarterTimePeriod(year: year, quarter: quarter);
final dataQtps = data.inflationTimeSeries.data.keys;
final doesYearHaveOlderQuarterInPoints = dataQtps.any(
(dataQtp) =>
dataQtp.year == qtp.year && dataQtp.quarter < qtp.quarter,
);
if (doesYearHaveOlderQuarterInPoints) return 'Q$quarter';
return qtp.toString();
},
lineDataList: [
LineChartDataEntity(
title: null,
textAboveMarkerBuilder: (y) => '${y.toStringAsFixed(2)}%',
color: MyTheme.topicColor(data.id.topic),
data: data.inflationTimeSeries.data.map(
(qtp, val) => MapEntry(
qtp.year + qtp.quarter * 0.25,
val.toDouble(),
),
),
isShaded: false,
),
],
),
);
final inflationBySubGroupChart = data.inflationBySubGroup == null
? null
: GraphHolderCard(
HorizontalBarChart(
data.inflationBySubGroup!.data.map(
(name, val) => MapEntry(
context.translateFromTuple(name),
(
barEndText: '${val.toStringAsFixed(2)}%',
value: val,
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Inflation By Sub Group',
'التضخم الاعلى بالمجموعات ا لفرعية',
),
subtitleText: data.inflationBySubGroup!.year.toString(),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
inflationByMajorGroupsChart,
18.verticalSpace,
inflationTimeSeriesChart,
if (inflationBySubGroupChart != null) ...[
18.verticalSpace,
inflationBySubGroupChart,
],
],
);
},
);
}

View File

@ -1,270 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class TradeBody extends ConsumerWidget {
const TradeBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<TradeEntity>(
builder: (data) {
final boxesGrid = Column(
children: [
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.totalTrade,
iconPath: IndicatorIconAssetPath.handshake,
title: context.translate(
'Total Trade',
'إجمالي التجارة',
),
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.totalImports,
iconPath: IndicatorIconAssetPath.import,
title: context.translate(
'Total imports',
'إجمالي الاستيراد',
),
),
),
],
),
18.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.totalNonOilExport,
iconPath: IndicatorIconAssetPath.export,
title: context.translate(
'Total Non-Oil Exports',
'إجمالي الصادرات غير النفطية',
),
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.totalReExport,
iconPath: IndicatorIconAssetPath.reExport,
title: context.translate(
'Total Re-Exports',
'إجمالي إعادة التصدير',
),
),
),
],
),
],
);
final topTradePartners = GraphHolderCard(
overrideHorizontalPadding: 12,
SingleChildScrollView(
child: Column(
children: List.generate(
tradePartnersSorted(data).length,
(index) {
final e =
tradePartnersSorted(data).entries.elementAt(index);
final country = e.key;
final flag = SizedBox(
height: 12,
width: 12,
child: Image.asset(
country.flagImgPath,
height: 12,
cacheHeight: 36,
),
);
final countryName = Text(
'${index + 1}. ${context.translate(
country.nameEN,
country.nameAR,
)}',
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 12,
fontWeight: FontWeight.w500,
),
);
final tradeValue = Text(
e.value.toStringWithCommas(
abbreviateIfAbove10powN: 5,
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 12,
fontWeight: FontWeight.w500,
),
);
return ColoredBox(
color: index % 2 == 0
? Colors.transparent
: Colors.grey.shade200,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
flag,
4.horizontalSpace,
Expanded(
child: countryName,
),
14.horizontalSpace,
tradeValue,
],
),
),
);
},
),
),
),
titleText: context.translate(
'Top Trade Partners',
'أعلى شركاء تجاريين',
),
subtitleText: context.translateFromTuple(
data.topTradePartners.subtitle,
),
);
final tradeTrendAnnually = GraphHolderCard(
titleText: context.translate(
'Trade Trend Annually',
'اتجاه التجارة سنويا',
),
subtitleText:
context.translateFromTuple(data.tradeTrendAnnually.subtitle),
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: context.translate(
'Import',
'يستورد',
),
color: MyTheme.topicColor(data.id.topic).shade50,
data: data.tradeTrendAnnually.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.import.toDouble(),
),
),
),
LineChartDataEntity(
title: context.translate(
'Export',
'يصدّر',
),
color: MyTheme.topicColor(data.id.topic).shade400,
data: data.tradeTrendAnnually.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.export.toDouble(),
),
),
),
LineChartDataEntity(
title: context.translate(
'ReExport',
'إعادة التصدير',
),
color: MyTheme.topicColor(data.id.topic).shade900,
data: data.tradeTrendAnnually.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.reExport.toDouble(),
),
),
),
],
),
);
final topTradeCommodities = data.topTradeCommodities == null
? null
: GraphHolderCard(
HorizontalBarChart(
data.topTradeCommodities!.data.map(
(name, qty) => MapEntry(
context.translateFromTuple(name),
(
value: qty.toDouble(),
barEndText: qty.toAbbreviatedString(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
graphHeightDelta: 900,
titleText: context.translate(
'Top Trade Commodities',
'أعلى سلع تجارية',
),
subtitleText: context.translateFromTuple(
data.topTradeCommodities!.subtitle,
),
);
final footNoteText = FootNoteText(
context.translate(
'Source: UAE Customs',
'المصدر :جمارك الإمارات',
),
);
return Column(
children: [
boxesGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
topTradePartners,
18.verticalSpace,
tradeTrendAnnually,
if (topTradeCommodities != null) ...[
18.verticalSpace,
topTradeCommodities,
],
],
);
},
);
Map<ISOCountry, num> tradePartnersSorted(TradeEntity data) {
final list = data.topTradePartners.data.entries.toList()
..sort(
(a, b) => b.value.compareTo(a.value),
);
return Map.fromEntries(
list.map(
(e) => MapEntry(
ISOCountry.fromNameEN(e.key.en),
e.value,
),
),
);
}
}

View File

@ -1,130 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class AreaOfNationalReservesBody extends ConsumerWidget {
const AreaOfNationalReservesBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<NaturalReservesEntity>(
builder: (data) {
final specificDetailBoxes = Column(
children: [
IntrinsicHeight(
child: Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.protectedAreaCount,
title: context.translate(
'Protected Area Count',
'عدد المناطق المحمية',
),
iconPath: IndicatorIconAssetPath.hillArea,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.totalProtectedMarineArea,
title: context.translate(
'Total Protected Marine Area',
'إجمالي المساحة البحرية المحمية',
),
iconPath: IndicatorIconAssetPath.marine,
),
),
],
),
),
18.verticalSpace,
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.totalProtectedTerrestrialArea,
title: context.translate(
'Total Protected Terrestrial Area',
'إجمالي المساحة البرية المحمية',
),
iconPath: IndicatorIconAssetPath.park,
),
),
],
);
final protectedAreaCountByAnnouncementYearCard = GraphHolderCard(
titleText: context.translate(
'Protected Area Count By Announcement Year',
'عدد المناطق المحمية حسب سنة الإعلان',
),
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
textAboveMarkerBuilder: (y) => y.toInt().toString(),
title: context.translate(
'Count by Announcement Year',
'العد حسب سنة الإعلان',
),
data: data.protectedAreaCountByAnnouncementYear.map(
(key, value) => MapEntry(
key.toDouble(),
value.countByAnnouncementYear.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade900,
),
LineChartDataEntity(
textAboveMarkerBuilder: (y) => y.toInt().toString(),
title: context.translate(
'Protected Areas Cumulative Count',
'العدد التراكمي للمناطق المحمية',
),
data: data.protectedAreaCountByAnnouncementYear.map(
(key, value) => MapEntry(
key.toDouble(),
value.protectedAreasCumulativeCount.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade50,
),
],
),
);
final distributionOfProtectedAreasCard = GraphHolderCard(
ThemedPieChartSingular(
materialColor: MyTheme.topicColor(data.id.topic),
data: {
context.translate('Marine', 'البحرية'):
data.totalProtectedMarineArea.value.toDouble(),
context.translate('Terrestrial', 'ساكن الأرض'):
data.totalProtectedTerrestrialArea.value.toDouble(),
},
),
titleText: context.translate(
'Distribution of Protected Areas',
'توزيع المحميات الطبيعية',
),
);
return Column(
children: [
specificDetailBoxes,
18.verticalSpace,
protectedAreaCountByAnnouncementYearCard,
18.verticalSpace,
distributionOfProtectedAreasCard,
],
);
},
);
}

View File

@ -1,201 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class OilBody extends ConsumerWidget {
const OilBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<OilEntity>(
builder: (data) {
final boxGrid = Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.crudeOilProduction,
title: context.translate(
'Crude Oil Production',
'إنتاج النفط الخام',
),
iconPath: IndicatorIconAssetPath.drilling,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.crudeOilExports,
title: context.translate(
'Crude Oil Exports',
'صادرات النفط الخام',
),
iconPath: IndicatorIconAssetPath.crudeOil,
),
),
],
);
final crudeOilProductionAndExportsAnnualTrendGraph = GraphHolderCard(
titleText: context.translate(
'Crude Oil Production & Exports Annual Trend',
'اتجاه إنتاج وتصدير النفط الخام السنوي',
),
subtitleText: '(1000 b/d)',
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: context.translate(
'Exports',
'صادرات',
),
data: data.crudeOilProductionAndExportsAnnualTrend.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.exports.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade900,
isShaded: false,
),
LineChartDataEntity(
title: context.translate(
'Production',
'إنتاج',
),
data: data.crudeOilProductionAndExportsAnnualTrend.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.production.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade50,
isShaded: false,
),
],
),
);
final refinedOilProductsGraph = data.refinedOilProducts == null
? null
: GraphHolderCard(
titleText: context.translate(
'Refined Oil Products',
'المنتجات النفطية المكررة',
),
subtitleText: context.translateFromTuple(
data.refinedOilProducts!.subtitle,
),
HorizontalBarChart(
color: MyTheme.topicColor(data.id.topic),
data.refinedOilProducts!.data.map(
(name, val) => MapEntry(
context.translateFromTuple(name),
(
barEndText: val.toString(),
value: val.toDouble(),
),
),
),
),
);
final veryLongNameGraph =
data.naturalGasProductionExportsAndImportsAnnualTrend == null
? null
: GraphHolderCard(
titleText: context.translate(
'Natural Gas Production, Exports & Imports Annual Trend',
'الاتجاه السنوي لإنتاج وصادرات وواردات الغاز الطبيعي',
),
subtitleText: '(${context.translate(
'Million Cubic Meters',
'مليون متر مكعب',
)})',
ThemedLineChart(
lineDataList: [
LineChartDataEntity(
title: context.translate(
'Exports',
'صادرات',
),
data: data
.naturalGasProductionExportsAndImportsAnnualTrend!
.data
.map(
(year, vals) => MapEntry(
year.toDouble(),
vals.exports.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade50,
),
LineChartDataEntity(
title: context.translate(
'Imports',
'الواردات',
),
data: data
.naturalGasProductionExportsAndImportsAnnualTrend!
.data
.map(
(year, vals) => MapEntry(
year.toDouble(),
vals.imports.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade400,
),
LineChartDataEntity(
title: context.translate(
'Production',
'إنتاج',
),
data: data
.naturalGasProductionExportsAndImportsAnnualTrend!
.data
.map(
(year, vals) => MapEntry(
year.toDouble(),
vals.production.toDouble(),
),
),
color: MyTheme.topicColor(data.id.topic).shade900,
),
],
),
);
final footNoteText = FootNoteText(
context.translate(
'The source of data is according to Organization of the Petroleum Exporting Countries (OPEC)',
'اعتمادا على بيانات منظمة الدول المصدرة للنفط (أوبك)',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
crudeOilProductionAndExportsAnnualTrendGraph,
if (refinedOilProductsGraph != null) ...[
18.verticalSpace,
refinedOilProductsGraph,
],
if (veryLongNameGraph != null) ...[
18.verticalSpace,
veryLongNameGraph,
],
],
);
},
);
}

View File

@ -1,95 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class WaterBody extends ConsumerWidget {
const WaterBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<WaterEntity>(
builder: (data) {
final boxGrid = Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.production,
title: context.translate('Production', 'الإنتاج'),
iconPath: IndicatorIconAssetPath.desalinatedWaterProduction,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.productionGrowth,
title: context.translate(
'Production Growth',
'نمو الإنتاج',
),
iconPath:
IndicatorIconAssetPath.desalinatedWaterProductionGrowth,
),
),
],
);
final annualProductionGraph = GraphHolderCard(
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: null,
data: data.annualProduction.data.map(
(year, qty) => MapEntry(year.toDouble(), qty.toDouble()),
),
color: MyTheme.topicColor(data.id.topic),
),
],
),
titleText: context.translate(
'Annual Production',
'الإنتاج السنوي',
),
subtitleText: 'MCM',
);
final qtyOfProducedWaterByEntityGraph = GraphHolderCard(
titleText: context.translate(
'Quantity of Produced Water by Entity',
'كمية المياه المنتجة حسب الجهة',
),
subtitleText: context.translateFromTuple(
data.qtyOfProducedWaterByEntity.subtitle,
),
SingleChildScrollView(
child: ThemedPieChartPlural(
data: data.qtyOfProducedWaterByEntity.data.map(
(locale, qty) => MapEntry(
context.translateFromTuple(locale),
qty.toDouble(),
),
),
materialColor: MyTheme.topicColor(data.id.topic),
),
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
annualProductionGraph,
18.verticalSpace,
qtyOfProducedWaterByEntityGraph,
],
);
},
);
}

View File

@ -1,141 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class ElectricityConsumptionBody extends ConsumerWidget {
const ElectricityConsumptionBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<ElectricityConsumptionEntity>(
builder: (data) {
final boxGrid = Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.consumption,
title: context.translate(
'Consumption',
'الاستهلاك',
),
iconPath: IndicatorIconAssetPath.consumption,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.consumptionGrowth,
title: context.translate(
'Consumption Growth',
'نمو الاستهلاك',
),
iconPath: IndicatorIconAssetPath.consumptionGrowth,
),
),
],
);
final annualConsumptionGraph = GraphHolderCard(
titleText: context.translate(
'Annual Consumption',
'الاستهلاك السنوي',
),
subtitleText: context.translate(
'(Gigawatt Hours)',
'(ساعات جيجاوات)',
),
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: null,
isShaded: false,
data: data.annualConsumption.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.toDouble(),
),
),
color: MyTheme.topicColor(
data.id.topic,
),
),
],
),
);
final electricityConsumptionByEmirateGraph = GraphHolderCard(
titleText: context.translate(
'Electricity Consumption by Emirate',
'استهلاك الكهرباء حسب الإمارة',
),
subtitleText: context.translateFromTuple(
data.electricityConsumptionByEmirate.subtitle,
),
SingleChildScrollView(
child: ThemedPieChartPlural(
materialColor: MyTheme.topicColor(data.id.topic),
data: data.electricityConsumptionByEmirate.data.map(
(locale, value) => MapEntry(
context.translateFromTuple(locale),
value.toDouble(),
),
),
),
),
);
final electricityConsumptionBySectorGraph = GraphHolderCard(
HorizontalBarChart(
data.electricityConsumptionBySector.data.map(
(sector, qty) => MapEntry(
context.translateFromTuple(sector),
(
value: qty.toDouble(),
barEndText: qty.toStringWithCommas(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Electricity Consumption by Sector',
'استهلاك الكهرباء حسب القطاع',
),
subtitleText: context.translateFromTuple(
data.electricityConsumptionBySector.subtitle,
),
);
final footNoteText = FootNoteText(
context.translate(
'Disclaimer: Other Emirates include (Ajman - Ras al Khaimah - Al Fujairah - Umm Al Quwain)',
'تنويه: الإمارات الأخرى تشمل (عجمان - رأس الخيمة - الفجيرة - أم القيوين)',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
annualConsumptionGraph,
18.verticalSpace,
electricityConsumptionByEmirateGraph,
18.verticalSpace,
electricityConsumptionBySectorGraph,
18.verticalSpace,
],
);
},
);
}

View File

@ -1,141 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class ElectricityProductionBody extends ConsumerWidget {
const ElectricityProductionBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<ElectricityProductionEntity>(
builder: (data) {
final boxGrid = Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.production,
title: context.translate(
'Production',
'إنتاج',
),
iconPath: IndicatorIconAssetPath.consumption,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.productionGrowth,
title: context.translate(
'Production Growth',
'نمو الإنتاج',
),
iconPath: IndicatorIconAssetPath.consumptionGrowth,
),
),
],
);
final annualProductionGraph = GraphHolderCard(
titleText: context.translate(
'Annual Production',
'الإنتاج السنوي',
),
subtitleText: context.translate(
'(Gigawatt Hours)',
'(ساعات جيجاوات)',
),
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
title: null,
isShaded: false,
data: data.annualProduction.data.map(
(year, val) => MapEntry(
year.toDouble(),
val.toDouble(),
),
),
color: MyTheme.topicColor(
data.id.topic,
),
),
],
),
);
final electricityProductionByEmirateGraph = GraphHolderCard(
titleText: context.translate(
'Electricity Production by Emirate',
'إنتاج الكهرباء حسب الإمارة',
),
subtitleText: context.translateFromTuple(
data.electricityProductionByEmirate.subtitle,
),
SingleChildScrollView(
child: ThemedPieChartPlural(
materialColor: MyTheme.topicColor(data.id.topic),
data: data.electricityProductionByEmirate.data.map(
(locale, value) => MapEntry(
context.translateFromTuple(locale),
value.toDouble(),
),
),
),
),
);
final electricityProductionByEntityGraph = GraphHolderCard(
HorizontalBarChart(
data.electricityProductionByEntity.data.map(
(sector, qty) => MapEntry(
context.translateFromTuple(sector),
(
value: qty.toDouble(),
barEndText: qty.toStringWithCommas(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Electricity Production by Sector',
'Electricity Production by Sector',
),
subtitleText: context.translateFromTuple(
data.electricityProductionByEntity.subtitle,
),
);
final footNoteText = FootNoteText(
context.translate(
'Disclaimer: Other Emirates include (Ajman - Ras al Khaimah - Al Fujairah - Umm Al Quwain)',
'تنويه: الإمارات الأخرى تشمل (عجمان - رأس الخيمة - الفجيرة - أم القيوين)',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
annualProductionGraph,
18.verticalSpace,
electricityProductionByEmirateGraph,
18.verticalSpace,
electricityProductionByEntityGraph,
18.verticalSpace,
],
);
},
);
}

View File

@ -1,156 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_vertical_bar_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class GeneralEducationBody extends ConsumerWidget {
const GeneralEducationBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<GeneralEducationEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.students,
title: context.translate(
'Students',
'الطلبة',
),
iconPath: IndicatorIconAssetPath.reading,
footnote: context.translate(
'The values represented are from early childhood to post-secondary non tertiary',
'يرجى العلم بأن عدد الطلاب يحتوي على اجمالي عدد الطلبة من مرحلة تعليم الطفولة المبكرة الى تعليم ما بعد الثانوي (غير عالي)',
),
),
),
18.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.teachers,
title: context.translate(
'Teachers',
'المعلمون',
),
iconPath: IndicatorIconAssetPath.teachers,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.institutions,
title: context.translate(
'Institutions',
'المؤسسات',
),
iconPath: IndicatorIconAssetPath.institutions,
),
),
],
),
],
);
final studentsByGenderGraph = GraphHolderCard(
titleText: context.translate(
'Students by Gender',
'الطلبة حسب النوع الإجتماعي',
),
subtitleText: data.studentsByGender.period,
ThemedPieChartSingular(
data: {
context.translate(
'Males',
'ذكور',
): data.studentsByGender.male.toDouble(),
context.translate(
'Females',
'إناث',
): data.studentsByGender.female.toDouble(),
},
perSegmentColor: {
context.translate(
'Males',
'ذكور',
): Colors.blue,
context.translate(
'Females',
'إناث',
): Colors.pink,
},
),
);
final institutionsByAcademicYear = GraphHolderCard(
ThemedVerticalBarChart(
{'': data.institutionsByAcademicYear},
materialColor: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Institutions by Academic Year',
'المؤسسات حسب العام الدراسي',
),
);
final teachersByGenderGraph = GraphHolderCard(
titleText: context.translate(
'Teachers by Gender',
'المعلمون حسب النوع الإجتماعي',
),
subtitleText: data.teachersByGender.period,
ThemedPieChartSingular(
data: {
context.translate(
'Males',
'ذكور',
): data.teachersByGender.male.toDouble(),
context.translate(
'Females',
'إناث',
): data.teachersByGender.female.toDouble(),
},
perSegmentColor: {
context.translate(
'Males',
'ذكور',
): Colors.blue,
context.translate(
'Females',
'إناث',
): Colors.pink,
},
),
);
final footNoteText = FootNoteText(
context.translate(
'The source of the data is Labour Force Survey Results/FCSC and relies on the UNPD Current Population data.',
'ومصدر السكان يعتمد على بيانات تعداد السكان الحالية من شعبة السكان لدى الأمم المتحدة FCSC/مصدر البيانات هو نتائج مسح القوى العاملة',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
studentsByGenderGraph,
18.verticalSpace,
institutionsByAcademicYear,
18.verticalSpace,
teachersByGenderGraph,
],
);
},
);
}

View File

@ -1,170 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class HigherEducationBody extends ConsumerWidget {
const HigherEducationBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<HigherEducationEntity>(
builder: (data) {
final boxGrid = Column(
children: [
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.students,
title: context.translate(
'Students',
'الطلبة',
),
iconPath: IndicatorIconAssetPath.reading,
),
),
18.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.graduates,
title: context.translate(
'Graduates',
'الخريجون',
),
iconPath: IndicatorIconAssetPath.graduate,
),
),
],
),
18.verticalSpace,
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.academicStaff,
title: context.translate(
'Academic Staff',
'الكادر التعليمى',
),
iconPath: IndicatorIconAssetPath.teachers,
),
),
],
);
final studentsByGenderGraph = GraphHolderCard(
titleText: context.translate(
'Students by Gender',
'الطلبة حسب النوع الإجتماعي',
),
subtitleText: data.studentsByGender.period,
ThemedPieChartSingular(
data: {
context.translate(
'Males',
'ذكور',
): data.studentsByGender.male.toDouble(),
context.translate(
'Females',
'إناث',
): data.studentsByGender.female.toDouble(),
},
perSegmentColor: {
context.translate(
'Males',
'ذكور',
): Colors.blue,
context.translate(
'Females',
'إناث',
): Colors.pink,
},
),
);
final academicStaffByGenderGraph = GraphHolderCard(
titleText: context.translate(
'Academic Staff by Gender',
'الكادر التعليمى حسب النوع الإجتماعي',
),
subtitleText: data.academicStaffByGender.period,
ThemedPieChartSingular(
perSegmentColor: {
context.translate(
'Males',
'ذكور',
): Colors.blue,
context.translate(
'Females',
'إناث',
): Colors.pink,
},
data: {
context.translate(
'Males',
'ذكور',
): data.academicStaffByGender.male.toDouble(),
context.translate(
'Females',
'إناث',
): data.academicStaffByGender.female.toDouble(),
},
),
);
final graduatedStudentsByFieldAndGenderGraph = GraphHolderCard(
graphHeightDelta: 200,
HorizontalGroupedBarChart(
data.graduatedStudentsByFieldAndGender.map(
(major, vals) {
return MapEntry(
context.translateFromTuple(major),
{
'Male': (
barColor: Colors.blue,
value: vals.male,
barEndText: vals.male.toStringWithCommas(),
),
'Female': (
barColor: Colors.pink,
value: vals.female,
barEndText: vals.female.toStringWithCommas(),
),
},
);
},
),
),
titleText: context.translate(
'Graduated Students by Field and Gender',
'الخريجون حسب التخصص و النوع الإجتماعي',
),
);
final footNoteText = FootNoteText(
context.translate(
'The source of the data is Labour Force Survey Results/FCSC and relies on the UNPD Current Population data.',
'ومصدر السكان يعتمد على بيانات تعداد السكان الحالية من شعبة السكان لدى الأمم المتحدة FCSC/مصدر البيانات هو نتائج مسح القوى العاملة',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
studentsByGenderGraph,
18.verticalSpace,
academicStaffByGenderGraph,
18.verticalSpace,
graduatedStudentsByFieldAndGenderGraph,
],
);
},
);
}

View File

@ -1,203 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/horizontal_bar_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_vertical_bar_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class HospitalsBody extends ConsumerWidget {
const HospitalsBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<HospitalsEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.numHospitals,
title: context.translate(
'Hospitals',
'المستشفيات',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
8.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.numGov,
title: context.translate(
'Government',
'الحكومية',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
8.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.numPrivate,
title: context.translate(
'Private',
'الخاصة',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
],
),
18.verticalSpace,
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.numClinicsAndCenters,
title: context.translate(
'Clinics and Centers',
'العيادات و المراكز الصحية',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
8.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.numClinicsAndCentersGov,
title: context.translate(
'Government',
'حكومة',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
8.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.numClinicsAndCentersPrivate,
title: context.translate(
'Private',
'الخاصة',
),
iconPath: IndicatorIconAssetPath.bigHospital,
),
),
],
),
],
);
final healthCareProfessionalsBySpecializationGraph = GraphHolderCard(
subtitleText: context.translateFromTuple(
data.healthcareProfessionalsBySpecialization.subtitle,
),
HorizontalBarChart(
data.healthcareProfessionalsBySpecialization.data.map(
(origin, qty) => MapEntry(
context.translateFromTuple(origin),
(
value: qty.toDouble(),
barEndText: qty.toStringWithCommas(),
),
),
),
color: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Healthcare Professionals by Specialization',
'متخصصوا الرعاية الصحية حسب التخصص',
),
);
final hospitalBedsBySectorGraph = GraphHolderCard(
titleText: context.translate(
'Hospital Beds by Sector',
'أسرة المستشفيات حسب القطاع',
),
subtitleText: context.translateFromTuple(
data.hospitalBedsBySector.subtitle,
),
ThemedPieChartSingular(
materialColor: MyTheme.topicColor(data.id.topic),
data: data.hospitalBedsBySector.data.map(
(name, qty) => MapEntry(
context.translateFromTuple(name),
qty.toDouble(),
),
),
),
);
final patientsByPatientTypeAndSectorGraph =
data.patientsByPatientTypeAndSector == null
? null
: GraphHolderCard(
subtitleText: context.translateFromTuple(
data.hospitalBedsBySector.subtitle,
),
ThemedVerticalBarChart(
{
context.translate(
'Government',
'الحكومية',
): {
context.translate(
'Inpatients',
'المرضى الداخليين',
): data.patientsByPatientTypeAndSector!.data
.government.inpatients,
context.translate(
'Outpatients',
'العيادات الخارجية',
): data.patientsByPatientTypeAndSector!.data
.government.outpatients,
},
context.translate(
'Private',
'الخاصة',
): {
context.translate(
'Inpatients',
'المرضى الداخليين',
): data.patientsByPatientTypeAndSector!.data.private
.inpatients,
context.translate(
'Outpatients',
'العيادات الخارجية',
): data.patientsByPatientTypeAndSector!.data.private
.outpatients,
},
},
materialColor: MyTheme.topicColor(data.id.topic),
),
titleText: context.translate(
'Patients by Patient Type and Sector',
'المرضى حسب نوع المريض والقطاع',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
healthCareProfessionalsBySpecializationGraph,
18.verticalSpace,
hospitalBedsBySectorGraph,
if (patientsByPatientTypeAndSectorGraph != null) ...[
18.verticalSpace,
patientsByPatientTypeAndSectorGraph,
],
],
);
},
);
}

View File

@ -1,166 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
import 'package:uae_stat/presentation/components/charts/themed_distribution_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/footnote_text.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class LaborForceBody extends ConsumerWidget {
const LaborForceBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<LaborForceEntity>(
builder: (data) {
final boxGrid = Column(
children: [
SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.participationRateTotal,
footnote: context.translate(
'(Labor Force/Population Age 15+)',
'(المشتغلون/السكان 15+)',
),
title: context.translate(
'Participation Rate',
'معدل المشاركة',
),
iconPath: IndicatorIconAssetPath.laborForce,
),
),
8.verticalSpace,
Row(
children: [
Expanded(
child: SpecificDetailBoxView(
data.participationRateMales,
title: context.translate(
'Males',
'ذكور',
),
iconPath: IndicatorIconAssetPath.male,
),
),
8.horizontalSpace,
Expanded(
child: SpecificDetailBoxView(
data.participationRateFemales,
title: context.translate(
'Females',
'إناث',
),
iconPath: IndicatorIconAssetPath.female,
),
),
],
),
],
);
final maritalDistGraph = GraphHolderCard(
titleText: context.translate(
'Distribution by Marital Status',
'الحالة الاجتماعية',
),
ThemedDistributionChart(
doSort: false,
middleText: context.translate(
'Marital Status',
'الحالة الاجتماعية',
),
labelBuilder: (isStart, value) =>
'${value.toAbbreviatedString()}%',
data.distributionByMaritalStatus.map(
(key, vals) => MapEntry(
context.translateFromTuple(key),
(
left: vals.male,
right: vals.female,
),
),
),
leftColor: Colors.blue,
rightColor: Colors.pink,
),
);
final ageDistGraph = GraphHolderCard(
titleText: context.translate('Age', 'العمر'),
graphHeightDelta: 500,
ThemedDistributionChart(
middleText: context.translate('Years', 'سنوات'),
doSort: false,
labelBuilder: (isStart, value) =>
'${value.toAbbreviatedString()}%',
data.distributionByAge.map(
(key, vals) => MapEntry(
context.translateFromTuple(
(
en: key.en.replaceAll(' years', ''),
ar: key.ar.replaceAll(' سنة', ''),
),
),
(
left: vals.male,
right: vals.female,
),
),
),
leftColor: Colors.blue,
rightColor: Colors.pink,
),
);
final eduLevelGraph = GraphHolderCard(
titleText: context.translate(
'Education Level',
'المستوى التعليمي',
),
graphHeightDelta: 500,
ThemedDistributionChart(
doSort: false,
middleText: context.translate(
'Education Level',
'مستوى التعليم',
),
labelBuilder: (isStart, value) =>
'${value.toAbbreviatedString()}%',
data.distributionByEducationLevel.map(
(key, vals) => MapEntry(
context.translateFromTuple(key),
(
left: vals.male,
right: vals.female,
),
),
),
leftColor: Colors.blue,
rightColor: Colors.pink,
),
);
final footNoteText = FootNoteText(
context.translate(
'Data source: labor force surveys\nLabor Force/Unemployment/Employment are calculated for ages 15+ according to international standards',
'المصدر: مسح القوى العاملة\nمقاييس القوى العاملةالبطالةالمشتغلين يتم حسابها للاعمار 15+ إلتزاما بالمنهجيات الدولية ',
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
footNoteText,
18.verticalSpace,
maritalDistGraph,
18.verticalSpace,
eduLevelGraph,
18.verticalSpace,
ageDistGraph,
],
);
},
);
}

View File

@ -1,117 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/my_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/series_asset_icon_path.dart';
import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart';
import 'package:uae_stat/presentation/components/charts/themed_line_chart.dart';
import 'package:uae_stat/presentation/components/charts/themed_pie_chart.dart';
import 'package:uae_stat/presentation/components/indicators/graph_holder_card.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/specific_detail_box_view.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/uae_numbers_routes/components/uae_numbers_scaffold.dart';
class PopulationBody extends ConsumerWidget {
const PopulationBody({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) =>
UaeNumbersScaffold<PopulationEntity>(
builder: (data) {
final boxGrid = SizedBox(
width: double.infinity,
child: SpecificDetailBoxView(
data.population,
title: context.translate(
'Population',
'السكان',
),
iconPath: IndicatorIconAssetPath.population,
),
);
// final popDistGraph = data.populationDistribution.data == null
// ? null
// : GraphHolderCard(
// title: context.translate(
// 'Population Distribution',
// 'التوزيع السكاني',
// ),
// ThemedDistributionChart(
// data.populationDistributionData!.map(
// (key, vals) => MapEntry(
// context.translateFromTuple(key),
// (
// left: vals.male,
// right: vals.female,
// ),
// ),
// ),
// leftColor: Colors.blue,
// rightColor: Colors.pink,
// ),
// );
final popGrowthGraph = GraphHolderCard(
titleText: context.translate(
'Population Growth',
'النمو السكاني',
),
ThemedLineChart(
yAxisTitleBuilder: (value) => value.toInt().toString(),
lineDataList: [
LineChartDataEntity(
isShaded: false,
title: null,
data: data.populationGrowth.map(
(year, qty) => MapEntry(year.toDouble(), qty.toDouble()),
),
color: MyTheme.topicColor(data.id.topic),
),
],
),
);
final popByGenderGraph = GraphHolderCard(
titleText: context.translate(
'Population by Gender',
'السكان حسب النوع الاجتماعي',
),
ThemedPieChartSingular(
data: {
context.translate(
'Males',
'ذكور',
): data.genderDistribution.male.toDouble(),
context.translate(
'Females',
'إناث',
): data.genderDistribution.female.toDouble(),
},
perSegmentColor: {
context.translate(
'Males',
'ذكور',
): Colors.blue,
context.translate(
'Females',
'إناث',
): Colors.pink,
},
),
);
return Column(
children: [
boxGrid,
18.verticalSpace,
// if (popDistGraph != null) ...[
// popDistGraph,
// 18.verticalSpace,
// ],
popGrowthGraph,
18.verticalSpace,
popByGenderGraph,
],
);
},
);
}

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../../../../../infrastructure/services/img_asset_paths/banner_asset_path.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';

View File

@ -7,9 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';
// import 'package:uae_stat/l10n/app_localizations.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';

View File

@ -7,9 +7,7 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';
// import 'package:uae_stat/l10n/app_localizations.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';

View File

@ -8,7 +8,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';

View File

@ -7,7 +7,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';

View File

@ -8,7 +8,6 @@ import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/config/theme/theme_provider.dart';
import 'package:uae_stat/config/toggle_lang_service.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
// import 'package:flutter_gen/gen_l10n/app_localizations.dart';

View File

@ -1,785 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/packages/go_router.dart';
import 'package:uae_stat/presentation/components/my_drawer.dart';
import 'package:mailer/mailer.dart';
import 'package:mailer/smtp_server.dart';
import 'package:uae_stat/presentation/components/space.dart';
import '../../../config/my_theme.dart';
import '../../../domain/use_cases/preferences_use_case.dart';
import '../../components/lang_toggle.dart';
import '../../components/my_bottom_nav_bar.dart';
import '../../components/my_toggle.dart';
class FeedbackForm extends StatefulWidget {
const FeedbackForm({super.key});
@override
_FeedbackFormState createState() => _FeedbackFormState();
}
class _FeedbackFormState extends State<FeedbackForm>
with WidgetsBindingObserver {
final _pb =
PocketBase(apiUrl); // Initialize PocketBase client
// final _pb =
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
final TextEditingController _feedbackController = TextEditingController();
double _easeOfUseRating = 0;
double _qualityRating = 0;
double _designRating = 0;
double _redundancyRating = 0;
int? _selectedEmojiIndex;
bool _isFeedbackSubmitted = false; // Track if feedback was submitted
bool _isFeedbackFailed = false; // New flag for failed submission
bool _isSmileySelected = true; // Track if smiley is selected
dynamic configEmail;
List<Map<String, dynamic>> get _emojiOptions => [
{
"icon": Icons.sentiment_very_dissatisfied,
"label": context.translate("Terrible", "رهيب"), // Translate here
"value": 1,
},
{
"icon": Icons.sentiment_dissatisfied,
"label": context.translate("Bad", "سيء"), // Translate here
"value": 2,
},
{
"icon": Icons.sentiment_neutral,
"label": context.translate("Okay", "تمام"), // Translate here
"value": 3,
},
{
"icon": Icons.sentiment_satisfied,
"label": context.translate("Good", "جيد"), // Translate here
"value": 4,
},
{
"icon": Icons.sentiment_very_satisfied,
"label": context.translate("Amazing", "مدهش"), // Translate here
"value": 5,
},
];
static const int _characterLimit = 1200;
final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$');
bool _hasError = false;
String _errorMessage = '';
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_loadFeedbackText();
_feedbackController.addListener(_handleTextChange);
fetchEmailConfiguration();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.detached ||
state == AppLifecycleState.paused) {
_removeFeedbackText();
}
}
// Detect when navigating to another page
@override
void didPushNext() {
// Called when a new page is pushed on top of FeedbackPage
_removeFeedbackText();
}
Future<void> _saveFeedbackText(
int emojiIndex, String ratingKey, double ratingValue) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('feedbackText', _feedbackController.text);
await prefs.setInt('selected_emoji_index', emojiIndex);
await prefs.setDouble(ratingKey, ratingValue);
print('one save');
}
Future<void> _saveAllRatings() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble('ease_of_use_rating', _easeOfUseRating);
await prefs.setDouble('quality_rating', _qualityRating);
await prefs.setDouble('design_rating', _designRating);
await prefs.setDouble('redundancy_rating', _redundancyRating);
}
Future<void> _loadFeedbackText() async {
final prefs = await SharedPreferences.getInstance();
// Step 1: Load values from SharedPreferences into variables
final feedbackText = prefs.getString('feedbackText') ?? '';
final savedIndex = prefs.getInt('selected_emoji_index');
final easeOfUseRating = prefs.getDouble('ease_of_use_rating') ?? 0;
final qualityRating = prefs.getDouble('quality_rating') ?? 0;
final designRating = prefs.getDouble('design_rating') ?? 0;
final redundancyRating = prefs.getDouble('redundancy_rating') ?? 0;
// Step 2: Populate fields with values without immediately clearing storage
setState(() {
_feedbackController.text = feedbackText;
if (savedIndex != null) {
_selectedEmojiIndex = savedIndex;
_isSmileySelected = true; // Indicating the user selected an emoji
}
_easeOfUseRating = easeOfUseRating;
_qualityRating = qualityRating;
_designRating = designRating;
_redundancyRating = redundancyRating;
});
// Step 3: Clear storage after a slight delay
Future.delayed(Duration(milliseconds: 50), () async {
await prefs.remove('feedbackText');
await prefs.remove('selected_emoji_index');
await prefs.remove('ease_of_use_rating');
await prefs.remove('quality_rating');
await prefs.remove('design_rating');
await prefs.remove('redundancy_rating');
});
}
// Remove feedback text from local storage
Future<void> _removeFeedbackText() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('feedbackText');
await prefs.remove('selected_emoji_index');
await prefs.remove('ease_of_use_rating');
await prefs.remove('quality_rating');
await prefs.remove('design_rating');
await prefs.remove('redundancy_rating');
}
void _handleTextChange() {
String currentText = _feedbackController.text;
// Enforce character limit and truncate excess on paste
if (currentText.length > _characterLimit) {
_feedbackController.text = currentText.substring(0, _characterLimit);
_feedbackController.selection = TextSelection.fromPosition(
TextPosition(offset: _feedbackController.text.length),
);
}
// Validate characters
if (!_allowedCharacters.hasMatch(currentText)) {
setState(() {
_hasError = true;
_errorMessage = "Invalid Characters";
});
} else {
setState(() {
_hasError = false;
_errorMessage = '';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: const MyDrawer(),
appBar: AppBar(
backgroundColor: const Color(0xFFf8f9ff),
title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')),
actions: [
Align(
alignment: AlignmentDirectional.topEnd,
child: LangToggle(
onSaveFeedback: _saveFeedbackText,
onLoadFeedback: _loadFeedbackText,
selectedEmojiIndex: _selectedEmojiIndex ?? 0,
easeOfUseRating: _easeOfUseRating,
qualityRating: _qualityRating,
designRating: _designRating,
redundancyRating: _redundancyRating,
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: _isFeedbackSubmitted
? _buildThankYouMessage()
: _isFeedbackFailed
? _buildFailureMessage()
: _buildFeedbackForm(),
),
),
);
}
Widget _buildFeedbackForm() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
context.translate(
'Do you have a suggestion or had any problem? Let us know.',
'هل لديك اقتراح أو واجهت أي مشكلة؟ اسمحوا لنا أن نعرف.',
),
style: const TextStyle(
fontSize: 18,
color: Color(0xFF898C81),
fontFamily: 'Roboto',
fontWeight: FontWeight.w700,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Text(
context.translate('How was your experience with us today?',
'كيف كانت تجربتك معنا اليوم؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
),
),
const SizedBox(height: 10),
Container(
decoration: BoxDecoration(
border: Border.all(
color: _isSmileySelected ? Colors.transparent : Colors.red,
width: 1.5,
),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(_emojiOptions.length, (index) {
return _buildEmojiButton(
icon: _emojiOptions[index]["icon"],
label: _emojiOptions[index]["label"],
index: index,
);
}),
),
if (!_isSmileySelected) // Show the error message if no smiley is selected
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
context.translate(
'Please rate your experience before submitting your feedback.',
'يرجى تقييم تجربتك قبل تقديم ملاحظاتك.',
),
style: TextStyle(color: Colors.red, fontSize: 14),
),
),
],
),
),
const SizedBox(height: 30),
Text(
context.translate('How good did we do in these aspects?',
'ما مدى جودة ما قمنا به في هذه الجوانب؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
),
),
const SizedBox(height: 20),
_buildRatingRow(
context.translate('Ease of use', 'سهولة الاستخدام'),
_easeOfUseRating,
(rating) {
setState(() {
_easeOfUseRating = rating;
});
},
),
_buildRatingRow(
context.translate('Quality', 'جودة'),
_qualityRating,
(rating) {
setState(() {
_qualityRating = rating;
});
},
),
_buildRatingRow(
context.translate('Design', 'تصميم'),
_designRating,
(rating) {
setState(() {
_designRating = rating;
});
},
),
_buildRatingRow(
context.translate('Redundant', 'متكرر'),
_redundancyRating,
(rating) {
setState(() {
_redundancyRating = rating;
});
},
),
const SizedBox(height: 20),
TextField(
controller: _feedbackController,
minLines: 5, // Start with 5 lines
maxLines: null, // Allows the field to expand automatically
maxLength: _characterLimit,
decoration: InputDecoration(
hintText: context.translate(
'Tell us how we can improve', 'أخبرنا كيف يمكننا تحسين'),
border: OutlineInputBorder(
borderSide: BorderSide(
color: _hasError ? Colors.red : Color(0xFF7296BE),
),
),
errorText:
_hasError ? _errorMessage : null, // Show error below field
),
onChanged: (text) {
// Trigger re-validation and character limit on each change
_handleTextChange();
},
),
const SizedBox(height: 20),
Center(
child: ElevatedButton(
onPressed: _submitFeedback,
style: ButtonStyle(
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 5.5),
),
textStyle: WidgetStatePropertyAll(
TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
backgroundColor: WidgetStatePropertyAll(
MyTheme.topicColor(IndicatorTopic.environment),
),
foregroundColor: const WidgetStatePropertyAll(
Colors.white,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(
'Submit',
'يُقدِّم',
),
),
6.horizontalSpace,
const Icon(Icons.chevron_right_outlined),
],
),
),
),
],
);
}
Widget _buildThankYouMessage() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.thumb_up, size: 80, color: Color(0xFF7DAFBC)),
const SizedBox(height: 20),
Text(
"Thank You",
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
"For your valuable feedback, we truly appreciate your input!",
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () => context
.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
child: const Text("Go to Home"),
),
],
),
);
}
// New widget for failure message
Widget _buildFailureMessage() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.error, size: 80, color: Colors.red),
const SizedBox(height: 20),
const Text("Failed to Share Feedback",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
const Text("Your feedback could not be submitted. Please try again.",
style: TextStyle(fontSize: 16), textAlign: TextAlign.center),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () {
setState(() {
_isFeedbackFailed = false; // Reset failure state
});
},
child: const Text("Retry"),
),
],
),
);
}
Widget _buildEmojiButton({
required IconData icon,
required String label,
required int index,
}) {
bool isSelected = _selectedEmojiIndex == index;
return Column(
children: [
IconButton(
icon: Icon(icon, size: 40),
color: isSelected ? Colors.green : Colors.amber,
onPressed: () {
setState(() {
_selectedEmojiIndex = index;
_isSmileySelected = true; // Hide error when a smiley is selected
});
},
),
Text(
label,
style: TextStyle(
fontWeight: FontWeight.w400,
color: isSelected ? Colors.green : Color(0xFF000000),
),
),
],
);
}
Widget _buildRatingRow(
String label,
double rating,
Function(double) onRatingUpdate,
) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: const TextStyle(
fontSize: 14,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF898C81),
)),
RatingBar.builder(
initialRating: rating,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 30,
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Colors.amber,
),
onRatingUpdate: onRatingUpdate,
unratedColor: Color(0xFF8E8E8E),
),
],
);
}
Future<void> _submitFeedback() async {
if (_selectedEmojiIndex == null) {
setState(() {
_isSmileySelected = false;
});
return;
} else {
setState(() {
_isSmileySelected = true;
});
}
if (_hasError) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Please correct the errors before submitting.")),
);
return;
}
final feedbackData = {
"ease_of_use": _easeOfUseRating,
"quality": _qualityRating,
"design": _designRating,
"redundancy": _redundancyRating,
"emoji_rating": _emojiOptions[_selectedEmojiIndex!]
["label"], // Emoji rating
"feedback": _feedbackController.text,
};
try {
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final adminToken = adminAuth.token;
final response = await _pb
.collection('feedback')
.create(body: feedbackData, headers: {'Authorization': adminToken});
if (response != null) {
final createdTime = response.created;
// Send email
await sendFeedbackEmail(feedbackData, createdTime);
await _removeFeedbackText();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Feedback submitted successfully!")),
);
setState(() {
_isFeedbackSubmitted = true;
_isFeedbackFailed = false;
_resetFeedbackForm();
});
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Failed to submit feedback: $e")),
);
setState(() {
_isFeedbackFailed = true; // Update state to show failure message
});
}
}
void _resetFeedbackForm() {
_easeOfUseRating = 0;
_qualityRating = 0;
_designRating = 0;
_redundancyRating = 0;
_selectedEmojiIndex = null;
_feedbackController.clear();
}
Future<void> fetchEmailConfiguration() async {
try {
// Fetch data from the email_configuration collection
final response =
await _pb.collection('email_configuration').getFullList();
// Filter the data where the label is "Feedback"
final feedbackConfig = response.firstWhere(
(item) =>
item.data['label'] == 'Feedback', // Accessing the 'data' property
);
// Check if a match is found
if (feedbackConfig != null) {
configEmail = feedbackConfig.data['email']; // Access the 'email' value
} else {
print('No feedback configuration found.');
}
} catch (e) {
print('Error fetching email configuration: $e');
}
}
Future<void> sendFeedbackEmail(
Map<String, dynamic> feedbackData, createdTime) async {
String username = 'emailapikey'; // Your SMTP username (API key)
String password =
'PHtE6r0MFu66jTQp8BAFsP7sH5TwNd4v/+02KwBH5ItACvAES01Tot4okDawqhoiB/FEHfaey4Nvteyf5ePQJG28YW9OCWqyqK3sx/VYSPOZsbq6x00auVwYd0zUVY7pe9ds0yLTvNraNA=='; // Your SMTP password
final smtpServer = SmtpServer('smtp.zeptomail.in',
port: 587,
username: username,
password: password,
ssl: false, // Use TLS
ignoreBadCertificate:
true); // Set to true if you're testing with a self-signed certificate
// Check if additional feedback was provided
String additionalFeedback = feedbackData["feedback"]?.isNotEmpty == true
? feedbackData["feedback"]
: "No additional feedback provided.";
// Format separately for date and time
// Parse the string to DateTime
// Parse the input as UTC and convert to DateTime in UTC timezone
DateTime feedbackDateTime = DateTime.parse(createdTime).toUtc();
// Format date as DD-MM-YYYY in UTC
final String formattedDate =
DateFormat('dd-MM-yyyy').format(feedbackDateTime);
// Format to the desired output: "dd.MM.yyyy HH:mm 'UTC'"
final DateFormat formatter = DateFormat("dd.MM.yyyy HH:mm 'UTC'");
String formattedDateTime = formatter.format(feedbackDateTime);
String email = configEmail; // Direct assignment
// Create the email message
final message = Message()
..from = Address('bbone@venbait.in', 'FCSC')
..recipients.add(email) // Set the recipient email
..subject = 'UAE Stats Feedback'
..text = '''
Dear [App Owner/Admin],
You have received new feedback from a user through the mobile application.
User Details:
1. Name: Guest
2. Date of Submission: $formattedDate
3. Time of Submission: $formattedDateTime
Feedback:
1. How was your experience with us today? Rating: ${feedbackData["emoji_rating"]}
2. How did we perform in key areas?
1. Ease of Use: ${feedbackData["ease_of_use"]}
2. Quality: ${feedbackData["quality"]}
3. Design: ${feedbackData["design"]}
4. Redundancy: ${feedbackData["redundancy"]}
3. Additional Feedback:
1. $additionalFeedback
Thank you,
The FCSC App Team
''';
try {
// Send the email
print("Message details:");
print("From: ${message.from}");
print("To: ${message.recipients}");
print("Subject: ${message.subject}");
print("Body: ${message.text}");
final sendReport = await send(message, smtpServer);
print('Message sent: ' + sendReport.toString());
} catch (e) {
print('Message not sent: $e');
// Handle the error as needed
}
}
@override
void dispose() {
_feedbackController.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
class LangToggle extends ConsumerWidget {
final Future<void> Function(
int emojiIndex, String ratingKey, double ratingValue) onSaveFeedback;
final Future<void> Function() onLoadFeedback;
final int selectedEmojiIndex;
final double easeOfUseRating;
final double qualityRating;
final double designRating;
final double redundancyRating;
const LangToggle({
required this.onSaveFeedback,
required this.onLoadFeedback,
required this.selectedEmojiIndex,
required this.easeOfUseRating,
required this.qualityRating,
required this.designRating,
required this.redundancyRating,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isOn = context.language == LanguageLocale.enUS;
return MyToggle(
isOn: isOn,
onTap: () async {
await onSaveFeedback(
selectedEmojiIndex,
'ease_of_use_rating',
easeOfUseRating,
);
await onSaveFeedback(
selectedEmojiIndex,
'quality_rating',
qualityRating,
);
await onSaveFeedback(
selectedEmojiIndex,
'design_rating',
designRating,
);
await onSaveFeedback(
selectedEmojiIndex,
'redundancy_rating',
redundancyRating,
);
final languageAfter = isOn ? LanguageLocale.arAE : LanguageLocale.enUS;
final grs = GoRouterState.of(context);
grs.pathParameters['locale'] = languageAfter.toString();
context.go(grs.pathWithParameters);
ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
(prefs) => prefs.copyWith(language: languageAfter),
);
await onLoadFeedback(); // Reload feedback text after toggling language
},
knobTextWhenOn: 'ع',
knobTextWhenOff: 'EN',
pathColorWhenOn: Color(0xFFD9D9D9),
pathColorWhenOff: Color(0xFFD9D9D9),
knobTextStyleWhenOn: TextStyle(
color: Color(0xFF010101),
fontWeight: FontWeight.bold,
),
knobTextStyleWhenOff: TextStyle(
color: Color(0xFF010101),
fontWeight: FontWeight.bold,
),
);
}
}

View File

@ -1,275 +0,0 @@
import 'package:flutter/material.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/my_drawer.dart';
class ManageUserRouter extends StatefulWidget {
@override
State<ManageUserRouter> createState() => _ManageUserRouterState();
}
class _ManageUserRouterState extends State<ManageUserRouter> {
// Sample data for the table
final List<User> userData = List.generate(
5,
(index) => User(
userName: 'Conan Keller ${index + 1}',
emailId: 'ConanKeller${index + 1}@gmail.com',
registrationDate: '18/07/2024',
status: 'Pending',
),
);
// Sorting state
int _sortColumnIndex = 0;
bool _isAscending = true;
// Method to sort the data based on a column
void _sort<T>(Comparable<T> Function(User user) getField, int columnIndex,
bool ascending) {
setState(() {
_sortColumnIndex = columnIndex;
_isAscending = ascending;
userData.sort((a, b) {
final aValue = getField(a);
final bValue = getField(b);
return ascending
? Comparable.compare(aValue, bValue)
: Comparable.compare(bValue, aValue);
});
});
}
// Define dropdown items
final List<String> statusOptions = ['Approved', 'Denied', 'Pending'];
// Method to get status color
Color getStatusColor(String status) {
switch (status) {
case 'Approved':
return Colors.green;
case 'Denied':
return Colors.red;
case 'Pending':
return Colors.blue;
default:
return Colors.black;
}
}
//Method to show a confirmation dialog when status is changed
Future<void> _showConfirmationDialog(User user, String newStatus) async {
double myheight = MediaQuery.of(context).size.height;
return showDialog<void>(
context: context,
barrierDismissible: false, // User must tap button to dismiss dialog
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), // Rounded corners
),
contentPadding: EdgeInsets.zero, // Ensure no padding issues with close icon
content: Stack(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: myheight/30,),
Text(
'Are you sure you want to change \n the status to $newStatus?',
style: TextStyle(fontSize: 15),
textAlign: TextAlign.center,
),
],
),
),
Positioned(
right: 0,
top: 0,
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
),
),
],
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton(
onPressed: () {
Navigator.of(context)
.pop(); // Close dialog without changing status
},
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10.0), // Adjust the radius as needed
),
side: BorderSide(
color: Colors.blue, // Set the outline color
width: 2.0, // Set the border width
),
),
child: Text('No'),
),
TextButton(
onPressed: () {
setState(() {
user.status = newStatus; // Change status
});
Navigator.of(context).pop();
},
style: TextButton.styleFrom(
backgroundColor: Colors.blue, // Set background color
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0), // Set text color
)),
child: Text(
'Yes',
style: TextStyle(color: Colors.white),
),
),
],
)
],
);
},
);
}
@override
Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height;
return Scaffold(
drawer: MyDrawer(),
appBar: AppBar(
title: const Text('Manage User',
style: TextStyle(fontWeight: FontWeight.bold)),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0),
child: Column(
children: [
TextField(
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.only(left: 12, right: 8),
child: Icon(
Icons.search,
color: Color(0xFFC3C6CB),
size: 20, // Optional: to match text height
),
),
prefixIconConstraints: BoxConstraints(
minHeight: 40,
minWidth: 40,
),
hintText: 'Search',
hintStyle: TextStyle(
color: Color(0xFFC3C6CB),
fontSize: 14,
height: 1.2, // Helps center text vertically
),
contentPadding: EdgeInsets.only(top: 14), // Adjust as needed to center text
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
),
SizedBox(height: myheight / 40),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
sortColumnIndex: _sortColumnIndex,
sortAscending: _isAscending,
columns: [
DataColumn(
label: const Text('User Name'),
onSort: (columnIndex, ascending) => _sort(
(user) => user.userName, columnIndex, ascending),
numeric: false,
),
DataColumn(
label: const Text('Email ID'),
onSort: (columnIndex, ascending) =>
_sort((user) => user.emailId, columnIndex, ascending),
numeric: false,
),
DataColumn(
label: const Text('Registration Date'),
onSort: (columnIndex, ascending) => _sort(
(user) => user.registrationDate,
columnIndex,
ascending),
numeric: false,
),
DataColumn(
label: const Text('Status'),
onSort: (columnIndex, ascending) =>
_sort((user) => user.status, columnIndex, ascending),
numeric: false,
),
],
rows: userData.map((user) {
return DataRow(
cells: [
DataCell(Text(user.userName)),
DataCell(Text(user.emailId)),
DataCell(Text(user.registrationDate)),
DataCell(
DropdownButton<String>(
value: user.status,
items: statusOptions.map((status) {
return DropdownMenuItem<String>(
value: status,
child: Text(
status,
style:
TextStyle(color: getStatusColor(status)),
),
);
}).toList(),
onChanged: (String? newStatus) {
if (newStatus != null) {
_showConfirmationDialog(user,
newStatus); // Show dialog for confirmation
}
},
),
),
],
);
}).toList(),
),
),
],
),
),
),
);
}
}
// User data model
class User {
final String userName;
final String emailId;
final String registrationDate;
String status;
User({
required this.userName,
required this.emailId,
required this.registrationDate,
this.status = 'Pending',
});
}

View File

@ -1,220 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/domain/use_cases/preferences_use_case.dart';
import 'package:uae_stat/domain/use_cases/user_use_case.dart';
import 'package:uae_stat/presentation/components/dialogs.dart';
import 'package:uae_stat/presentation/components/indicators/indicator_view.dart';
import 'package:uae_stat/presentation/components/ref_watch_handled.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
class MyFavoritesRoute extends HookConsumerWidget {
const MyFavoritesRoute({super.key});
Widget bookmarksLoader(
WidgetRef ref,
IList<IndicatorEnum> ids,
) =>
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisExtent: 90,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: ids.length,
padding: const EdgeInsets.fromLTRB(25, 25, 25, 125),
itemBuilder: (context, index) {
final e = ids[index];
final deleteBtn = ElevatedButton(
onPressed: () async {
final didConfirm = await context.confirmationDialog(
context.translate(
'Confirm removal',
'تأكيد الإزالة',
),
context.translate(
'If you remove ${e.titleEN} from favorites, you will no longer receive updates on it.',
'إذا قمت بإزالة ${e.titleAR} من المفضلة، فلن تتلقى تحديثات عليها بعد الآن',
),
);
if (didConfirm == false || didConfirm == null) return;
if (!context.mounted) return;
await context.loaderWithErrorDialog(
() => ref
.read(
preferencesUseCaseProvider.notifier,
)
.updatePreferences(
(prefs) => prefs.copyWith(
favoriteIndicators: prefs.favoriteIndicators.remove(e),
),
),
);
},
style: const ButtonStyle(
padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
),
minimumSize: WidgetStatePropertyAll(Size.zero),
backgroundColor: WidgetStatePropertyAll(Colors.red),
foregroundColor: WidgetStatePropertyAll(Colors.white),
),
child: const Icon(Icons.delete),
);
final stack = Stack(
alignment: Alignment.topCenter,
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
SizedBox(
width: double.infinity,
child: IndicatorView(e),
),
Positioned(
top: -15,
right: -15,
child: deleteBtn,
),
],
);
return stack;
},
);
@override
Widget build(BuildContext context, WidgetRef ref) {
final searchController = useTextEditingController();
final nBookmarks = ref
.watch(
preferencesUseCaseProvider.select(
(asyncPrefs) => asyncPrefs.whenData(
(data) =>
data.favoriteCountries.length +
data.favoriteIndicators.length +
data.favoriteReports.length,
),
),
)
.whenOrNull<int>();
final appBar = ThemedAppBar(
searchController:
nBookmarks == 0 || nBookmarks == null ? null : searchController,
titleText: [
context.translate(
'Favorites',
'المفضلات',
),
if (nBookmarks != null) '($nBookmarks)',
].join(' '),
);
final favoritesProvider = preferencesUseCaseProvider.select(
(asyncPrefs) => asyncPrefs.whenData(
(prefs) => prefs.favoriteIndicators,
),
);
final grid = RefWatchHandled(
favoritesProvider,
onData: (data) {
if (data.isEmpty) {
return Center(
child: Text(
context.translate(
'No favorites added yet.',
'لم تتم إضافة أي مفضلة حتى الآن',
),
style: TextStyle(
color: Colors.grey.shade600,
),
),
);
}
return ValueListenableBuilder(
valueListenable: searchController,
builder: (context, value, child) {
final cleanedSearchTerm = value.text
.replaceAll(' ', '')
.replaceAll(
RegExp(
r'[^\w\s]',
multiLine: true,
caseSensitive: false,
),
'',
)
.toLowerCase();
return bookmarksLoader(
ref,
data
.where(
(e) => e.matchSearch(cleanedSearchTerm),
)
.toIList(),
);
},
);
},
);
final disclaimer = RefWatchHandled(
userUseCaseProvider.select(
(asyncUser) => asyncUser.whenData(
(user) => user != null,
),
),
onData: (isLoggedIn) {
if (isLoggedIn) return const SizedBox.shrink();
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.go(
'/${context.language}/login',
),
child: Text(
context.translate(
'Login',
'تسجيل الدخول',
),
),
),
Text(
context.translate(
' to sync favorites across your devices.',
' لمزامنة المفضلة عبر أجهزتك',
),
),
],
),
],
);
},
);
final bodyContent = Column(
children: [
disclaimer,
Expanded(child: grid),
],
);
return ColoredBox(
color: Colors.white,
child: SafeArea(
bottom: false,
child: Column(
children: [
appBar,
Expanded(child: bodyContent),
],
),
),
);
}
}

View File

@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/domain/use_cases/notifications_use_case.dart';
import 'package:uae_stat/presentation/components/ref_watch_handled.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
class NotificationsRoute extends ConsumerWidget {
const NotificationsRoute({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final bodyContent = RefWatchHandled(
notificationsUseCaseProvider,
onData: (data) => ListView.separated(
itemBuilder: (context, index) {
final e = data[index];
return ListTile(
leading: const Icon(Icons.notifications),
title: Text(e.title),
subtitle: Text(e.content),
trailing: Text(
DateFormat('d/M/yy').format(e.dateTime),
),
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: data.length,
),
);
final body = Column(
children: [
ThemedAppBar(
titleText: context.translate(
'Notifications',
'إشعارات',
),
),
Expanded(
child: bodyContent,
),
],
);
return ColoredBox(
color: Colors.white,
child: SafeArea(child: body),
);
}
}

View File

@ -1,177 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/packages/list.dart';
import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
class PrivacyPolicyRoute extends HookConsumerWidget {
const PrivacyPolicyRoute({super.key});
static const _policies = {
'Information We Collect':
'''We collect the following user data from the App:
1. Screens visited and time spent on each screen
2. Data related to app crashes
3. Loading speeds
4. Use of Collected Data
The collected data is used solely to improve the user experience of the App. Specifically:
1. We analyze user behavior to optimize areas where users spend a significant amount of time.
2. Data related to app crashes is used to identify and reduce instances of app crashes.''',
'Third-Party Access':
'We do not provide third parties access to the collected data.',
'Security and Confidentiality':
'User data is securely stored and will not be shared with any external parties.',
'Protection of Privacy':
'We do not collect sensitive information from users.',
'Access, Update, and Deletion of Data':
'Users can access and update their profile information, including their name, within the App. They also have the option to delete their profile entirely from the profile page.',
'Use of Tracking Technologies':
'We may utilize tools like cookies and Google Analytics to track and analyze user interactions with the App.',
'Age Requirement': 'Users must be at least 16 years old to use the App.',
'Notification of Policy Changes':
'Users may be notified of any changes to this Privacy Policy via email.',
'Contact Us':
'If you have any questions or concerns regarding this Privacy Policy, please contact us through the feedback form.',
};
@override
Widget build(BuildContext context, WidgetRef ref) {
final expandedTopic = useState<String?>(null);
final body = Column(
children: [
ThemedAppBar(
titleText: context.translate(
'Terms & Privacy Policy',
'الشروط وسياسة الخصوصية',
),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(18, 0, 18, 25),
child: Column(
children: [
Text(
'Privacy Policy for UAE Stats',
style: TextStyle(
fontFamily: context.translate(
'Lato',
'NotoKufi',
),
fontWeight: FontWeight.bold,
),
),
12.verticalSpace,
Text(
'This Privacy Policy explains how the Federal Competitiveness and Statistics Center ("we", "our", or "us") collects, uses, and protects the information you provide when you use the "UAE Stats" mobile application (the "App"). By using the App, you agree to the terms and conditions of this Privacy Policy.',
textAlign: TextAlign.justify,
style: TextStyle(
fontFamily: context.translate(
'Lato',
'NotoKufi',
),
),
),
12.verticalSpace,
..._policies.entries.map<Widget>(
(e) {
final header = Padding(
padding: const EdgeInsetsDirectional.only(
start: 18,
end: 24,
top: 12,
bottom: 16,
),
child: Row(
children: [
Expanded(
child: Text(
e.key,
style: TextStyle(
fontFamily: context.translate(
'Lato',
'NotoKufi',
),
fontWeight: FontWeight.bold,
),
),
),
12.horizontalSpace,
AnimatedRotation(
turns: expandedTopic.value == e.key ? 0.5 : 0,
duration: kThemeAnimationDuration,
child: const Icon(
Icons.keyboard_arrow_down_outlined,
),
),
],
),
);
final footer = Padding(
padding: const EdgeInsets.only(
top: 12,
bottom: 24,
left: 24,
right: 24,
),
child: Text(
e.value,
textAlign: TextAlign.justify,
style: TextStyle(
fontFamily: context.translate(
'Lato',
'NotoKufi',
),
),
),
);
return GestureDetector(
onTap: () => expandedTopic.value != e.key
? expandedTopic.value = e.key
: expandedTopic.value = null,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.blue.shade50,
),
child: Column(
children: [
header,
AnimatedSize(
alignment: Alignment.topCenter,
duration: kThemeAnimationDuration,
child: SizedBox(
height: expandedTopic.value == e.key ? null : 0,
width: double.infinity,
child: footer,
),
),
],
),
),
);
},
).toList()
..intersperseDivider(
12.verticalSpace,
),
],
),
),
),
],
);
return ColoredBox(
color: Colors.white,
child: SafeArea(
bottom: false,
child: body,
),
);
}
}

View File

@ -1,286 +0,0 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/components/dialogs.dart';
import 'package:uae_stat/presentation/components/space.dart';
import '../../../../config/my_theme.dart';
import '../../../../domain/use_cases/auth_use_case.dart';
import '../../../../domain/use_cases/user_use_case.dart';
import '../../../components/ref_watch_handled.dart';
import '../../../components/themed_app_bar.dart';
class ProfileRoute extends HookConsumerWidget {
const ProfileRoute({super.key});
Widget fieldBuilder(
BuildContext context, {
required String title,
required String fieldText,
required TextEditingController controller,
bool enabled = true,
}) {
final titleText = Text(
title,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.bold,
),
);
controller.text = fieldText;
final textField = TextField(
controller: controller,
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
),
enabled: enabled,
maxLines: 2,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 16,
),
filled: enabled,
fillColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide(
color: const Color(0xff544C4C).withOpacity(0.14),
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: const Color(0xff544C4C).withOpacity(0.14),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: const Color(0xff544C4C).withOpacity(0.14),
),
),
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
titleText,
6.verticalSpace,
textField,
],
);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final nameCtl = useTextEditingController();
final emailCtl = useTextEditingController();
final bodyContent = RefWatchHandled(
userUseCaseProvider,
onData: (data) {
if (data == null) return const SizedBox();
nameCtl.text = data.name;
emailCtl.text = data.email;
final avatar = CircleAvatar(
radius: 65,
backgroundColor: MyTheme.topicColor(IndicatorTopic.economy),
foregroundColor: Colors.white,
child: data.imgUrl != null
? Image.network(data.imgUrl!)
: const Icon(
Icons.person,
size: 65 * 1.3,
),
);
final saveBtn = CupertinoButton.filled(
onPressed: () async {
if (data.name == nameCtl.text) return;
await context.loaderWithErrorDialog(
() => ref
.read(
userUseCaseProvider.notifier,
)
.updateProfile(
newName: nameCtl.text,
),
);
if (!context.mounted) return;
context.simpleDialog(
title: context.translate(
'Success!',
'نجاح!',
),
content: context.translate(
'Name changed to ${nameCtl.text}',
'تم تغير الاسم الى ${nameCtl.text}',
),
);
},
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(
'Save',
'يحفظ',
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
10.horizontalSpace,
const Icon(Icons.save),
],
),
);
final logoutBtn = CupertinoButton(
color: Colors.red,
onPressed: () async {
await context.loaderWithErrorDialog(
() => ref
.read(
authUseCaseProvider.notifier,
)
.logout(),
);
if (!context.mounted) return;
context.go('/${context.language}/login');
},
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(
'Logout',
'تسجيل خروج',
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
10.horizontalSpace,
const Icon(Icons.logout),
],
),
);
final resetPwBtn = CupertinoButton(
onPressed: () => context.go('/${context.language}/profile/reset-pw'),
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(
'Reset Password',
'إعادة تعيين كلمة المرور',
),
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
10.horizontalSpace,
const Icon(Icons.password),
],
),
);
final screenWidth = MediaQuery.of(context).size.width;
final listViewHorizontalPadding =
screenWidth < 700 ? 32 : 32 + (screenWidth - 700) / 2;
final bodyContent = ListView(
padding: EdgeInsets.symmetric(
horizontal: listViewHorizontalPadding.toDouble(),
),
children: [
36.verticalSpace,
avatar,
24.verticalSpace,
fieldBuilder(
context,
title: context.translate(
'Name',
'اسم',
),
fieldText: data.name,
controller: nameCtl,
),
25.verticalSpace,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: fieldBuilder(
context,
title: context.translate(
'Email',
'بريد إلكتروني',
),
enabled: false,
fieldText: data.email,
controller: emailCtl,
),
),
25.horizontalSpace,
resetPwBtn,
],
),
25.verticalSpace,
saveBtn,
25.verticalSpace,
logoutBtn,
],
);
return bodyContent;
},
);
final body = Column(
children: [
ThemedAppBar(
titleText: context.translate(
'My Profile',
'ملفي',
),
),
Expanded(
child: Container(color: Colors.lightGreenAccent, child: bodyContent),
),
],
);
final bgAdded = ColoredBox(
color: Colors.white,
child: SafeArea(
bottom: false,
child: body,
),
);
return bgAdded;
}
}

View File

@ -1,225 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:uae_stat/config/old_theme.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/domain/use_cases/user_use_case.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/background_asset_path.dart';
import 'package:uae_stat/presentation/components/dialogs.dart';
import 'package:uae_stat/presentation/components/space.dart';
class ResetPwRoute extends HookConsumerWidget {
const ResetPwRoute({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final screenWidth = MediaQuery.of(context).size.width;
final listViewHorizontalPadding =
screenWidth < 700 ? 15 : 15 + (screenWidth - 700) / 2;
final oldPwCtl = useTextEditingController();
final doObscureOld = useState<bool>(true);
final oldPwField = TextField(
obscureText: doObscureOld.value,
controller: oldPwCtl,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () => doObscureOld.value = !doObscureOld.value,
icon: Icon(
doObscureOld.value ? Icons.visibility_off : Icons.visibility,
),
),
),
);
final newPwCtl = useTextEditingController();
final doObscureNew = useState<bool>(true);
final newPwField = TextFormField(
obscureText: doObscureNew.value,
controller: newPwCtl,
validator: (value) {
if (value!.length < 10) {
return 'Passwords cannot be less than 10 characters.';
}
final containsNumberRegex = RegExp(r'\d');
if (!containsNumberRegex.hasMatch(value)) {
return 'Passwords must contain at least one number';
}
return null;
},
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () => doObscureNew.value = !doObscureNew.value,
icon: Icon(
doObscureNew.value ? Icons.visibility_off : Icons.visibility,
),
),
),
);
final confirmNewPwCtl = useTextEditingController();
final doObscureConfirmNew = useState<bool>(true);
final confirmNewPwField = TextFormField(
obscureText: doObscureConfirmNew.value,
controller: confirmNewPwCtl,
validator: (value) {
if (value != newPwCtl.text) {
return 'Confirmation field must match the new password.';
}
return null;
},
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () =>
doObscureConfirmNew.value = !doObscureConfirmNew.value,
icon: Icon(
doObscureConfirmNew.value ? Icons.visibility_off : Icons.visibility,
),
),
),
);
final formKey = GlobalKey<FormState>();
final confirmBtn = ElevatedButton(
child: const Text('Confirm Password Change'),
onPressed: () async {
final areInputsValid = formKey.currentState!.validate();
if (!areInputsValid) return;
final didConfirm = await context.confirmationDialog(
'Confirm Password Reset',
'You will be sent an email from where you can continue the process.',
);
if (didConfirm != true) return;
if (!context.mounted) return;
await context.loaderWithErrorDialog(
() => ref
.read(userUseCaseProvider.notifier)
.updatePw(oldPw: oldPwCtl.text, newPw: newPwCtl.text),
errorDialogBuilder: (
error, [
StackTrace? stackTrace,
]) async {
if (error is! PasswordResetError) return;
switch (error) {
case PasswordResetError.incorrectOldPw:
final email = ref.watch(userUseCaseProvider).when(
data: (data) => data!.email,
error: (error, stackTrace) => '!',
loading: () => '~',
);
return context.simpleDialog(
title: 'Incorrect password',
content:
'You must type the current password for $email in the old password field.',
);
}
},
);
await Fluttertoast.showToast(msg: 'Password changed successfully',
gravity: ToastGravity.CENTER,
);
if (!context.mounted) return;
context.pop();
},
);
final bodyContent = Form(
key: formKey,
child: ListView(
padding: EdgeInsets.symmetric(
horizontal: listViewHorizontalPadding.toDouble(),
),
children: [
24.verticalSpace,
oldPwField,
16.verticalSpace,
newPwField,
16.verticalSpace,
confirmNewPwField,
16.verticalSpace,
confirmBtn,
],
),
);
final body = Column(
children: [
const _AppBar(),
Expanded(
child: bodyContent,
),
],
);
final bgAdded = DecoratedBox(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(BackgroundAssetPath.defaultLight),
),
),
child: ColoredBox(
color: Colors.transparent,
child: SafeArea(
bottom: false,
child: body,
),
),
);
return bgAdded;
}
}
class _AppBar extends ConsumerWidget {
const _AppBar();
void _logout(BuildContext context) =>
context.go('/${context.language}/login');
@override
Widget build(BuildContext context, WidgetRef ref) {
const title = Text(
'Reset Password',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: OldTheme.economyDark,
),
);
final logoutBtn = CupertinoButton(
onPressed: () => _logout(context),
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: [
Text(
'Logout',
style: TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
10.horizontalSpace,
const Icon(
Icons.exit_to_app,
),
],
),
);
final body = Padding(
padding: const EdgeInsets.only(top: 10),
child: Column(
children: [
Row(
children: [
13.horizontalSpace,
const DrawerButton(),
const BackButton(),
const Expanded(child: title),
13.horizontalSpace,
logoutBtn,
],
),
],
),
);
return body;
}
}

View File

@ -1,100 +0,0 @@
// import 'package:flutter/material.dart';
// import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
// import 'package:flutter_hooks/flutter_hooks.dart';
// import 'package:hooks_riverpod/hooks_riverpod.dart';
// import 'package:video_player/video_player.dart';
//
// import 'package:uae_stat/domain/use_cases/language.dart';
// import 'package:uae_stat/infrastructure/services/img_asset_paths/user_guide_asset_path.dart';
// import 'package:uae_stat/presentation/components/space.dart';
// import 'package:uae_stat/presentation/components/themed_app_bar.dart';
//
// typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc});
//
// class UserGuideRoute extends HookConsumerWidget {
// const UserGuideRoute({super.key});
//
// static const List<_UserGuideType> _data = [
// (
// videoPath: UserGuideAssetPath.toggleLanguage,
// enDesc:
// 'You can toggle between Arabic and English by click the toggle button.',
// arDesc:
// 'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.',
// ),
// (
// videoPath: UserGuideAssetPath.searchIndicators,
// enDesc:
// 'You can browse indicators by category or use the search functionality',
// arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث',
// ),
// (
// videoPath: UserGuideAssetPath.bookmarkIndicators,
// enDesc:
// 'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.',
// arDesc:
// 'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.',
// ),
// ];
//
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final e = useState<_UserGuideType>(_data.first);
// final carouselOptions
// // final carouselOptions = CarouselOptions(
// // autoPlay: true,
// // clipBehavior: Clip.none,
// // onPageChanged: (index, reason) => e.value = _data[index],
// // aspectRatio: 9 / 17,
// // disableCenter: true,
// // enlargeCenterPage: true,
// // floatingIndicator: true,
// // autoPlayInterval: const Duration(seconds: 10),
// // );
// final bodyContent = Column(
// children: [
// Expanded(
// flex: 3,
// child: FlutterCarousel(
// options: carouselOptions,
// items: _data
// .map(
// (e) => VideoPlayer(
// VideoPlayerController.asset(
// e.videoPath,
// )..initialize(),
// ),
// )
// .toList(),
// ),
// ),
// Expanded(
// child: SingleChildScrollView(
// padding: const EdgeInsets.fromLTRB(36, 24, 36, 36),
// child: Text(
// context.translate(e.value.enDesc, e.value.arDesc),
// ),
// ),
// ),
// 98.verticalSpace,
// ],
// );
// final body = Column(
// children: [
// ThemedAppBar(
// titleText: context.translate(
// 'User Guide',
// 'دليل المستخدم',
// ),
// ),
// Expanded(
// child: bodyContent,
// ),
// ],
// );
// return ColoredBox(
// color: Colors.white,
// child: SafeArea(child: body),
// );
// }
// }