From 3b038f7b428f2c659a16690e7f8160ac44e0d594 Mon Sep 17 00:00:00 2001 From: VINISTAN Date: Thu, 9 Jan 2025 13:36:31 +0530 Subject: [PATCH 1/2] Marriage card Implemented --- lib/config/my_router.dart | 13 +- .../competitiveness.dart | 0 .../Country_Profile/country_profile.dart | 142 ++++++++ .../UAE_Numbers/Social/marriages.dart | 266 ++++++++++++++ .../{ => UAE_Numbers}/uae_numbers.dart | 36 +- .../country_profile.dart | 31 -- .../tab_routes/home_route.dart | 10 +- .../drawer_routes/custom_drawer_routes.dart | 329 +++++++++++++++--- pubspec.lock | 4 +- pubspec.yaml | 2 +- 10 files changed, 723 insertions(+), 110 deletions(-) rename lib/presentation/Screens/Bottom Navigation Pages/{ => Competitiveness}/competitiveness.dart (100%) create mode 100644 lib/presentation/Screens/Bottom Navigation Pages/Country_Profile/country_profile.dart create mode 100644 lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart rename lib/presentation/Screens/Bottom Navigation Pages/{ => UAE_Numbers}/uae_numbers.dart (91%) delete mode 100644 lib/presentation/Screens/Bottom Navigation Pages/country_profile.dart diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index 1e6d9a68..40b75655 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -294,9 +294,10 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/competitiveness.dart'; -import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/country_profile.dart'; -import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/uae_numbers.dart'; +import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Competitiveness/competitiveness.dart'; +import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/Country_Profile/country_profile.dart'; +import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/UAE_Numbers/Social/marriages.dart'; +import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/UAE_Numbers/uae_numbers.dart'; import 'package:uae_stat/presentation/Screens/auth_verification/registration.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/getting_started.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; @@ -426,5 +427,11 @@ final GoRouter router = GoRouter( builder: (context, state) => CountryProfile(), ), + + // UAE NUMBER Routes + GoRoute( + path: '/marriages', + builder: (context, state) => Marriages(), + ), ], ); diff --git a/lib/presentation/Screens/Bottom Navigation Pages/competitiveness.dart b/lib/presentation/Screens/Bottom Navigation Pages/Competitiveness/competitiveness.dart similarity index 100% rename from lib/presentation/Screens/Bottom Navigation Pages/competitiveness.dart rename to lib/presentation/Screens/Bottom Navigation Pages/Competitiveness/competitiveness.dart diff --git a/lib/presentation/Screens/Bottom Navigation Pages/Country_Profile/country_profile.dart b/lib/presentation/Screens/Bottom Navigation Pages/Country_Profile/country_profile.dart new file mode 100644 index 00000000..58a1c5ae --- /dev/null +++ b/lib/presentation/Screens/Bottom Navigation Pages/Country_Profile/country_profile.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; +import 'package:fl_chart/fl_chart.dart'; + +class CountryProfile extends StatelessWidget { + const CountryProfile({super.key}); + + @override + Widget build(BuildContext context) { + double myheight = MediaQuery.of(context).size.height; + double mywidth = MediaQuery.of(context).size.width; + return BaseScaffold( + title: Center( + child: SizedBox( + height: myheight / 5, + width: mywidth / 3, + child: Image(image: AssetImage('assets/logos/uae_stat.png')))), + body: CountryProfileWidget(), + ); + } +} +class CountryProfileWidget extends StatelessWidget { + const CountryProfileWidget({super.key}); + + @override + Widget build(BuildContext context) { + return HorizontalBarChart(); + } +} + + + + + +class HorizontalBarChart extends StatelessWidget { + final List xAxisData = ['2019', '2020', '2021', '2022', '2023']; + final List yAxisData = [2, 4, 1, 5, 3]; + + + @override + Widget build(BuildContext context) { + double myheight = MediaQuery.of(context).size.height; + return Scaffold( + appBar: AppBar(title: Text('Horizontal Bar Chart')), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: + Container( + height: myheight/4, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 8, + offset: Offset(0, 4), + ), + ], + ), + child: + Padding( + padding: const EdgeInsets.only(top: 16,left: 8.0,right: 8.0,bottom: 8.0), + child: BarChart( + BarChartData( + alignment: BarChartAlignment.spaceAround, + maxY: 8, + barTouchData: BarTouchData(enabled: false), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 2, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text('${value.toInt()}K'), + ); + }, + reservedSize: 60, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(xAxisData[value.toInt()]), + ); + }, + reservedSize: 40, + ), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hides top titles + ), + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hides right titles + ), + ), + gridData: FlGridData(show: false), + borderData: FlBorderData(show: false), + barGroups: List.generate( + xAxisData.length, + (index) => BarChartGroupData( + x: index, + barRods: [ + BarChartRodData( + toY: yAxisData[index], + color: Colors.blueAccent, + //colors: [Colors.blueAccent], + borderRadius: BorderRadius.circular(4), + width: 20, + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } +} + +void main() { + runApp(MaterialApp(home: HorizontalBarChart())); +} + + +// void main() { +// runApp(MaterialApp(home: HorizontalBarChart())); +// } + + + + + + + diff --git a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart new file mode 100644 index 00000000..0caad6ea --- /dev/null +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart @@ -0,0 +1,266 @@ +import 'package:flutter/material.dart'; +import 'package:uae_stat/presentation/components/constant/constant.dart'; +import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; +import 'package:fl_chart/fl_chart.dart'; + + +class Marriages extends StatelessWidget { + const Marriages({super.key}); + + @override + Widget build(BuildContext context) { + double myheight = MediaQuery.of(context).size.height; + double mywidth = MediaQuery.of(context).size.width; + final List xAxisData = ['2019', '2020', '2021', '2022', '2023']; + final List yAxisData = [2, 4, 1, 5, 3]; + return BaseScaffold( + //appbarActions: IconButton(onPressed: (){}, icon: Icon(Icons.arrow_back_ios_new)), + appbarColor: Color(0xFFC6BEB2), + title: Text("UAE Numbers",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),), + showBackButton: true, + body: SingleChildScrollView( + child: Column( + children : [ + Container( + height: myheight/4.5, + width: double.infinity, + color: Color(0xFFC6BEB2), + child: Padding(padding: EdgeInsets.only(left: 30,right: 30,top: 20,bottom: 10),child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SOCIAL",style: TextStyle(color: Colors.white,fontSize: 30),), + Container(width: mywidth/5,child: Divider(thickness: 10,color: Colors.white,)), + Text("Marriages",style: TextStyle(color: Colors.white,fontSize: 20),), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton(onPressed: (){}, icon: Icon(Icons.bookmark_add_outlined,color: Colors.white,size: 40,)), + IconButton(onPressed: (){}, icon: Icon(Icons.share,color: Colors.white,size: 30,)), + ],), + ], + ),) + ), + SingleChildScrollView(child: Container( + height: myheight, + color: Color(0xFFC6BEB2), + child: Padding( + padding: const EdgeInsets.only(top: 0.0,left: 10,right: 10,bottom: 20), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container(height: myheight/3.5,width: mywidth/2.5, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/icons/indicator/social/marriage.png', + height: myheight / 10, // This ensures the image fits within the container's height + width: myheight / 10, // You can adjust the size as needed + ), + Text("Total",style: TextStyle(fontWeight: FontWeight.bold,fontSize: 26),), + Text("2022",style: TextStyle(fontSize: 15,color: Colors.grey),), + Text("16759",style: TextStyle(fontSize: 26,fontWeight: FontWeight.bold,color: socialColor),), + ], + ), + ), + Container(height: myheight/3.5,width: mywidth/2.5, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/icons/indicator/social/marriage.png', + height: myheight / 10, // This ensures the image fits within the container's height + width: myheight / 10, // You can adjust the size as needed + ), + Text("Average",style: TextStyle(fontWeight: FontWeight.bold,fontSize: 26),), + Text("(2021 - 2022)",style: TextStyle(fontSize: 15,color: Colors.grey),), + Text("15987",style: TextStyle(fontSize: 26,fontWeight: FontWeight.bold,color: socialColor),), + ], + ), + ), + ], + ), + SizedBox(height: myheight/20,), + Container( + height: myheight/2, + width: mywidth/1.2, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + children: [ + SizedBox(height: myheight/30,), + Center(child: Text("Total Marriages by Marriage Type",style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), + ), + SizedBox(height: myheight/30,), + Container( + height: myheight/4, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 8, + offset: Offset(0, 4), + ), + ], + ), + child: + Padding( + padding: const EdgeInsets.only(top: 16,left: 8.0,right: 8.0,bottom: 8.0), + child: BarChart( + BarChartData( + alignment: BarChartAlignment.spaceAround, + maxY: 8, + barTouchData: BarTouchData(enabled: false), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 2, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text('${value.toInt()}K'), + ); + }, + reservedSize: 60, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(xAxisData[value.toInt()]), + ); + }, + reservedSize: 40, + ), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hides top titles + ), + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hides right titles + ), + ), + gridData: FlGridData(show: false), + borderData: FlBorderData(show: false), + barGroups: List.generate( + xAxisData.length, + (index) => BarChartGroupData( + x: index, + barRods: [ + BarChartRodData( + toY: yAxisData[index], + color: Colors.blueAccent, + //colors: [Colors.blueAccent], + borderRadius: BorderRadius.circular(4), + width: 20, + ), + ], + ), + ), + ), + ), + ), + ), + // Container( + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(12), + // boxShadow: [ + // BoxShadow( + // color: Colors.black12, + // blurRadius: 8, + // offset: Offset(0, 4), + // ), + // ], + // ), + // child: + // BarChart( + // BarChartData( + // alignment: BarChartAlignment.spaceAround, + // maxY: 20, + // barTouchData: BarTouchData(enabled: false), + // titlesData: FlTitlesData( + // leftTitles: AxisTitles( + // sideTitles: SideTitles( + // showTitles: true, + // getTitlesWidget: (value, meta) { + // return Padding( + // padding: const EdgeInsets.only(right: 8.0), + // child: Text('${value.toInt()}K'), + // ); + // }, + // reservedSize: 40, + // ), + // ), + // bottomTitles: AxisTitles( + // sideTitles: SideTitles( + // showTitles: true, + // getTitlesWidget: (value, meta) { + // return Padding( + // padding: const EdgeInsets.only(top: 8.0), + // child: Text(xAxisData[value.toInt()]), + // ); + // }, + // reservedSize: 40, + // ), + // ), + // ), + // gridData: FlGridData(show: false), + // borderData: FlBorderData(show: false), + // barGroups: List.generate( + // xAxisData.length, + // (index) => BarChartGroupData( + // x: index, + // barRods: [ + // BarChartRodData( + // toY: yAxisData[index], + // color: Colors.blueAccent, + // //colors: [Colors.blueAccent], + // borderRadius: BorderRadius.circular(4), + // width: 20, + // ), + // ], + // ), + // ), + // ), + // ), + // ), + + ], + ), + ), + ) + ], + ), + ), + + + ),) + ] + ), + ), mycenterTitle: false, + + ); + } +} + diff --git a/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart similarity index 91% rename from lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart rename to lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart index b1899ffa..5cb1bbb5 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/components/constant/constant.dart'; @@ -16,7 +17,7 @@ class UaeNumbers extends StatelessWidget { body: //EconomicDashboard(), //EconomyExpandTile() - uaenumberWidget()); + uaenumberWidget(),); } } @@ -83,8 +84,10 @@ class uaenumberWidget extends StatelessWidget { ]), buildCategoryTitle('Vital Statistics',socialColor), buildCardRow([ - buildCard(context,'Marriages', '96%', '2023',socialColor,socialColor,mywidth/2.5), - buildCard(context,'Divorces', '1.2K', '2024',socialColor,socialColor,mywidth/2.5), + InkWell(onTap : (){ + context.go('/marriages'); + },child: buildCard(context,'Marriages', '96%', '2023',socialColor,socialColor,mywidth/2.5)), + InkWell(onTap : (){},child: buildCard(context,'Divorces', '1.2K', '2024',socialColor,socialColor,mywidth/2.5)), ]), buildCategoryTitle('Education',socialColor), buildCardRow([ @@ -265,21 +268,22 @@ class _CustomExpandableTileState extends State { duration: Duration(milliseconds: 300), curve: Curves.easeInOut, width: double.infinity, - height: isExpanded ? myheight : 0, child: isExpanded - ? SingleChildScrollView( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(20)) - ), + ? IntrinsicHeight( + child: IntrinsicWidth( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(20)) + ), - padding: const EdgeInsets.all(10), - child: Column( - children: widget.children, - ), - ), - ) + padding: const EdgeInsets.all(10), + child: Column( + children: widget.children, + ), + ), + ), + ) : null, ), ), diff --git a/lib/presentation/Screens/Bottom Navigation Pages/country_profile.dart b/lib/presentation/Screens/Bottom Navigation Pages/country_profile.dart deleted file mode 100644 index 8d3f1081..00000000 --- a/lib/presentation/Screens/Bottom Navigation Pages/country_profile.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; - -class CountryProfile extends StatelessWidget { - const CountryProfile({super.key}); - - @override - Widget build(BuildContext context) { - double myheight = MediaQuery.of(context).size.height; - double mywidth = MediaQuery.of(context).size.width; - return BaseScaffold( - title: Center( - child: SizedBox( - height: myheight / 5, - width: mywidth / 3, - child: Image(image: AssetImage('assets/logos/uae_stat.png')))), - body: CountryProfileWidget() - ); - } -} -class CountryProfileWidget extends StatelessWidget { - const CountryProfileWidget({super.key}); - - @override - Widget build(BuildContext context) { - return Center(child: Text("COuntry Profile"),); - } -} - - - diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index cc4702c4..491dc171 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -177,11 +177,11 @@ class _MyHomePageState extends State { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; return BaseScaffold( - title: Center( - child: SizedBox( - height: myheight / 5, - width: mywidth / 3, - child: Image(image: AssetImage('assets/logos/uae_stat.png')))), + mycenterTitle: true, + title: SizedBox( + height: myheight / 5, + width: mywidth / 3, + child: Image(image: AssetImage('assets/logos/uae_stat.png'))), body: EconomyStats(), ); } diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index de8715ed..5b2a903e 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1,20 +1,215 @@ +// import 'package:flutter/material.dart'; +// import 'package:go_router/go_router.dart'; +// +// class BaseScaffold extends StatelessWidget { +// final Widget body; +// final Widget title; +// final Widget? appbarActions; +// final List? actions; +// final Color? appbarColor,mytitleColor; +// +// const BaseScaffold({ +// Key? key, +// required this.body, +// required this.title, +// this.actions, +// this.appbarColor, +// this.mytitleColor, +// this.appbarActions +// }) : super(key: key); +// // final Widget body; +// // +// // const BaseScaffold({required this.body}); +// +// @override +// Widget build(BuildContext context) { +// // Get the current route to highlight the active item +// String currentRoute = GoRouterState.of(context).matchedLocation; +// double myheight = MediaQuery.of(context).size.height; +// double mywidth = MediaQuery.of(context).size.width; +// return Scaffold( +// appBar: AppBar(title: title,backgroundColor: appbarColor, actions: [ +// IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), +// ], +// leading: appbarActions, +// +// ), +// +// drawer: Drawer( +// child: ListView( +// children: [ +// DrawerHeader( +// //decoration: BoxDecoration(color: Colors.blue), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.stretch, +// children: [ +// Row( +// children: [ +// SizedBox( +// width: mywidth / 8, +// child: Image( +// image: AssetImage('assets/logos/fcsc.png'))), +// ], +// ), +// Divider(), +// InkWell( +// onTap: () => context.go('/editProfile'), +// child: Row( +// children: [ +// SizedBox( +// width: mywidth / 8, +// child: Image( +// image: AssetImage( +// 'assets/edit_profile/profile.png'))), +// SizedBox( +// width: mywidth / 20, +// ), +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text('Mohammad'), +// Text('Mohammad@fcsc.com') +// ], +// ) +// ], +// ), +// ) +// ], +// ), +// ), +// ListTile( +// leading: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/drawer/message.png'))), +// title: Text('Feedback'), +// onTap: () => context.go('/feedback'), +// ), +// ListTile( +// leading: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/drawer/manageuser.png'))), +// title: Text('Manage User'), +// onTap: () => context.go('/manageuser'), +// ), +// +// ListTile( +// leading: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/drawer/book.png'))), +// title: Text('User Guide'), +// onTap: () => context.go('/user-guide'), +// ), +// ], +// ), +// ), +// bottomNavigationBar: BottomNavigationBar( +// currentIndex: _getSelectedIndex(currentRoute), +// onTap: (index) => _onItemTapped(context, index), +// type: BottomNavigationBarType.fixed, +// items: [ +// BottomNavigationBarItem( +// icon: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/bottom_bar/home.png'))), +// label: 'Home', +// ), +// BottomNavigationBarItem( +// icon: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/bottom_bar/uae_map.png'))), +// label: 'UAE Numbers', +// ), +// BottomNavigationBarItem( +// icon: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: +// AssetImage('assets/icons/bottom_bar/ranking.png'))), +// label: 'Competitiveness'), +// BottomNavigationBarItem( +// icon: SizedBox( +// height: myheight / 15, +// width: mywidth / 15, +// child: Image( +// image: AssetImage('assets/icons/bottom_bar/globe.png'))), +// label: 'Country Profile'), +// ], +// selectedItemColor: Colors.black, +// unselectedItemColor: Colors.grey, +// showUnselectedLabels: true, +// ), +// body: body, +// ); +// } +// +// //Map the current route to the selected index +// int _getSelectedIndex(String route) { +// switch (route) { +// case '/': +// return 0; +// case '/uaenumbers': +// return 1; +// case '/competitiveness': +// return 2; +// case '/countryprofile': +// return 3; +// default: +// return 0; +// } +// } +// +// //Handle navigation when an item is tapped +// void _onItemTapped(BuildContext context, int index) { +// switch (index) { +// case 0: +// context.go('/'); +// break; +// case 1: +// context.go('/uaenumbers'); +// break; +// case 2: +// context.go('/competitiveness'); +// break; +// case 3: +// context.go('/countryprofile'); +// break; +// } +// } +// } + + + import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class BaseScaffold extends StatelessWidget { final Widget body; final Widget title; + final bool? mycenterTitle; + final bool showBackButton; final List? actions; + final Color? appbarColor, mytitleColor; const BaseScaffold({ Key? key, required this.body, required this.title, this.actions, + this.appbarColor, + this.mytitleColor, + this.showBackButton = false, this.mycenterTitle = false, // Default is to show the drawer icon }) : super(key: key); - // final Widget body; - // - // const BaseScaffold({required this.body}); @override Widget build(BuildContext context) { @@ -23,76 +218,105 @@ class BaseScaffold extends StatelessWidget { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; return Scaffold( - appBar: AppBar(title: title, actions: [ - IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), - ]), + appBar: AppBar( + centerTitle: mycenterTitle, + title: title, + backgroundColor: appbarColor, + leading: + + Stack( + children: [ + + // Show the back button if showBackButton is true, otherwise show the drawer menu + if (showBackButton) + Positioned( + left: 20, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.arrow_back_ios_new), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + // Show the drawer icon in all cases + Positioned( + right: 10, + child: Builder( + builder: (BuildContext context) { + return IconButton( + color: showBackButton ? Colors.white : Colors.black, + icon: Icon(Icons.menu), + onPressed: () { + // Open the drawer using the Scaffold context + Scaffold.of(context).openDrawer(); + }, + ); + }, + ), + ), + ], + ), + + actions: actions, + ), drawer: Drawer( child: ListView( children: [ DrawerHeader( - //decoration: BoxDecoration(color: Colors.blue), + child: Container( child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage('assets/logos/fcsc.png'))), - ], - ), + SizedBox( + width: mywidth/5, + height: myheight/15, + child: Image.asset('assets/logos/fcsc.png')), Divider(), - InkWell( - onTap: () => context.go('/editProfile'), + SizedBox( + height: myheight/15, child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, // Ensures the row only takes up as much space as its children need children: [ - SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage( - 'assets/edit_profile/profile.png'))), - SizedBox( - width: mywidth / 20, + // The profile image with fixed size + Image.asset( + 'assets/edit_profile/profile.png', + height: myheight / 10, // This ensures the image fits within the container's height + width: myheight / 10, // You can adjust the size as needed + ), + + // Wrapping the Column in Flexible to ensure no overflow + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Mohammad'), + Text('Mohammad@fcsc.com'), + ], + ), ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Mohammad'), - Text('Mohammad@fcsc.com') - ], - ) ], ), - ) + ), ], - ), + + ),), + ), + SizedBox(height: myheight/60,), ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/message.png'))), + leading: Icon(Icons.feedback), title: Text('Feedback'), onTap: () => context.go('/feedback'), ), ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/manageuser.png'))), + leading: Icon(Icons.people), title: Text('Manage User'), onTap: () => context.go('/manageuser'), ), - ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/book.png'))), + leading: Icon(Icons.book), title: Text('User Guide'), onTap: () => context.go('/user-guide'), ), @@ -126,7 +350,7 @@ class BaseScaffold extends StatelessWidget { width: mywidth / 15, child: Image( image: - AssetImage('assets/icons/bottom_bar/ranking.png'))), + AssetImage('assets/icons/bottom_bar/ranking.png'))), label: 'Competitiveness'), BottomNavigationBarItem( icon: SizedBox( @@ -143,7 +367,6 @@ class BaseScaffold extends StatelessWidget { body: body, ); } - //Map the current route to the selected index int _getSelectedIndex(String route) { switch (route) { @@ -178,3 +401,5 @@ class BaseScaffold extends StatelessWidget { } } } + + diff --git a/pubspec.lock b/pubspec.lock index cc69ece0..566653a8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -385,10 +385,10 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" + sha256: "10ddaf334fe84d59333a12d153043e366f243e0bdfff2df0313e1e249f5bf926" url: "https://pub.dev" source: hosted - version: "0.69.2" + version: "0.70.1" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index a223630d..ab6ae569 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: injectable: ^2.3.2 riverpod_annotation: ^2.3.3 cupertino_icons: ^1.0.6 - fl_chart: ^0.69.0 + fl_chart: ^0.70.1 marquee: ^2.2.3 # dynamic_layouts: # git: From 5ac7afe1f103a520b35d8ad824f65c6867d4f8c1 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Fri, 10 Jan 2025 10:17:53 +0530 Subject: [PATCH 2/2] cahrt code merged --- lib/config/my_router.dart | 12 +- .../Bottom Navigation Pages/uae_numbers.dart | 198 +++--- lib/presentation/Screens/charts/chart.dart | 258 ++++--- .../charts/filters/search_filter_helper.dart | 33 + .../Screens/charts/models/chart_data.dart | 13 + .../Screens/charts/screens/chart_screen.dart | 600 +++++++++++++++++ .../Screens/charts/services/api_service.dart | 184 +++++ .../Screens/charts/widgets/chart_widget.dart | 632 ++++++++++++++++++ .../tab_routes/home_route.dart | 6 +- .../drawer_routes/custom_drawer_routes.dart | 4 +- pubspec.lock | 8 + pubspec.yaml | 1 + 12 files changed, 1732 insertions(+), 217 deletions(-) create mode 100644 lib/presentation/Screens/charts/filters/search_filter_helper.dart create mode 100644 lib/presentation/Screens/charts/models/chart_data.dart create mode 100644 lib/presentation/Screens/charts/screens/chart_screen.dart create mode 100644 lib/presentation/Screens/charts/services/api_service.dart create mode 100644 lib/presentation/Screens/charts/widgets/chart_widget.dart diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index c200ac19..6e2daf40 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -298,6 +298,8 @@ import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/compet import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/country_profile.dart'; import 'package:uae_stat/presentation/Screens/Bottom%20Navigation%20Pages/uae_numbers.dart'; import 'package:uae_stat/presentation/Screens/auth_verification/registration.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/routes/drawer_routes/Drawer%20Items/user_guide/getting_started.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; @@ -307,7 +309,6 @@ 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/charts/chart.dart'; import '../presentation/Screens/demo_home.dart'; import '../presentation/Screens/profilepage.dart'; import '../presentation/routes/auth_routes/login_route.dart'; @@ -355,6 +356,15 @@ final GoRouter router = GoRouter( return ChartScreen(dataSets: dataSets); }, ), + GoRoute( + path: '/chartScreen/:dataSets', + builder: (context, state) { + final dataSets = state.pathParameters['dataSets']!; + print('Router $dataSets'); + // return ChartScreen(dataSets: dataSets); + return ChartScreen1(dataSets: dataSets); + }, + ), GoRoute( path: '/mailverification', builder: (context, state) => EmailVerificationScreen( diff --git a/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart b/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart index b1899ffa..6787e132 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/uae_numbers.dart @@ -3,7 +3,6 @@ import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_r import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/components/constant/constant.dart'; - class UaeNumbers extends StatelessWidget { const UaeNumbers({super.key}); @@ -16,7 +15,7 @@ class UaeNumbers extends StatelessWidget { body: //EconomicDashboard(), //EconomyExpandTile() - uaenumberWidget()); + uaenumberWidget()); } } @@ -31,44 +30,82 @@ class uaenumberWidget extends StatelessWidget { padding: const EdgeInsets.all(16.0), child: ListView( children: [ - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(30.0), - border: Border.all(width: 1) - - ), - child: TextField( - decoration: InputDecoration( - hintText: "Search", - prefixIcon: Icon(Icons.search), - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0), - ), - ), - ), - SizedBox(height: myheight/35,), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(30.0), + border: Border.all(width: 1)), + child: TextField( + decoration: InputDecoration( + hintText: "Search", + prefixIcon: Icon(Icons.search), + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0), + ), + ), + ), + SizedBox( + height: myheight / 35, + ), CustomExpandableTile( title: 'ECONOMY', titleBackgroundColor: economyColor, children: [ - buildCategoryTitle('National Accounts',economyColor), + buildCategoryTitle('National Accounts', economyColor), buildCardRow([ - buildCard(context,'GDP (Constant)', '1.62T', '2022 (AED)',economyColor,economyColor,mywidth/2.5), - buildCard(context,'FDI', '1.45T', '2021 (AED)',economyColor,economyColor,mywidth/2.5), + buildCard(context, 'GDP (Constant)', '1.62T', '2022 (AED)', + economyColor, economyColor, mywidth / 2.5), + buildCard(context, 'FDI', '1.45T', '2021 (AED)', economyColor, + economyColor, mywidth / 2.5), ]), - buildCategoryTitle('International Trade',economyColor), + buildCategoryTitle('International Trade', economyColor), buildCardRow([ - buildCard(context,'Total Trade', '669.9B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/2.5), - buildCard(context,'Total Import', '686B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/2.5), + buildCard( + context, + 'Total Trade', + '669.9B', + 'Jan - Mar 2024 (AED)', + economyColor, + economyColor, + mywidth / 2.5), + buildCard( + context, + 'Total Import', + '686B', + 'Jan - Mar 2024 (AED)', + economyColor, + economyColor, + mywidth / 2.5), ]), buildCardRow([ - buildCard(context,'Total Export', '669.9B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/2.5), - buildCard(context,'Total ReExport', '686B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/2.5), + buildCard( + context, + 'Total Export', + '669.9B', + 'Jan - Mar 2024 (AED)', + economyColor, + economyColor, + mywidth / 2.5), + buildCard( + context, + 'Total ReExport', + '686B', + 'Jan - Mar 2024 (AED)', + economyColor, + economyColor, + mywidth / 2.5), ]), - buildCategoryTitle('Prices',economyColor), + buildCategoryTitle('Prices', economyColor), buildCardRow([ - buildCard(context,'Total Export', '669.9B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/1.2), + buildCard( + context, + 'Total Export', + '669.9B', + 'Jan - Mar 2024 (AED)', + economyColor, + economyColor, + mywidth / 1.2), ]), ], ), @@ -76,25 +113,35 @@ class uaenumberWidget extends StatelessWidget { title: 'SOCIAL', titleBackgroundColor: socialColor, children: [ - buildCategoryTitle('Population',socialColor), + buildCategoryTitle('Population', socialColor), buildCardRow([ - InkWell(onTap: (){},child: buildCard(context,'Population', '9.89M', '2024',socialColor,socialColor,mywidth/2.5)), - buildCard(context,'Population Growth', '2.1%', '2023',socialColor,socialColor,mywidth/2.5), + InkWell( + onTap: () {}, + child: buildCard(context, 'Population', '9.89M', '2024', + socialColor, socialColor, mywidth / 2.5)), + buildCard(context, 'Population Growth', '2.1%', '2023', + socialColor, socialColor, mywidth / 2.5), ]), - buildCategoryTitle('Vital Statistics',socialColor), + buildCategoryTitle('Vital Statistics', socialColor), buildCardRow([ - buildCard(context,'Marriages', '96%', '2023',socialColor,socialColor,mywidth/2.5), - buildCard(context,'Divorces', '1.2K', '2024',socialColor,socialColor,mywidth/2.5), + buildCard(context, 'Marriages', '96%', '2023', socialColor, + socialColor, mywidth / 2.5), + buildCard(context, 'Divorces', '1.2K', '2024', socialColor, + socialColor, mywidth / 2.5), ]), - buildCategoryTitle('Education',socialColor), + buildCategoryTitle('Education', socialColor), buildCardRow([ - buildCard(context,'General Education', '96%', '2023',socialColor,socialColor,mywidth/2.5), - buildCard(context,'Higher Education', '1.2K', '2024',socialColor,socialColor,mywidth/2.5), + buildCard(context, 'General Education', '96%', '2023', + socialColor, socialColor, mywidth / 2.5), + buildCard(context, 'Higher Education', '1.2K', '2024', + socialColor, socialColor, mywidth / 2.5), ]), - buildCategoryTitle('Health',socialColor), + buildCategoryTitle('Health', socialColor), buildCardRow([ - buildCard(context,'Hospital', '96%', '2023',socialColor,socialColor,mywidth/2.5), - buildCard(context,'Clinic and Centres', '1.2K', '2024',socialColor,socialColor,mywidth/2.5), + buildCard(context, 'Hospital', '96%', '2023', socialColor, + socialColor, mywidth / 2.5), + buildCard(context, 'Clinic and Centres', '1.2K', '2024', + socialColor, socialColor, mywidth / 2.5), ]), ], ), @@ -102,24 +149,32 @@ class uaenumberWidget extends StatelessWidget { title: 'ENVIRONMENT', titleBackgroundColor: environmentColor, children: [ - buildCategoryTitle('Agriculture',environmentColor), + buildCategoryTitle('Agriculture', environmentColor), buildCardRow([ - buildCard(context,'Crops - Total Area', '27°C', 'Average 2024',environmentColor,environmentColor,mywidth/2.5), - buildCard(context,'Livestock', '120mm', '2024',environmentColor,environmentColor,mywidth/2.5), + buildCard(context, 'Crops - Total Area', '27°C', 'Average 2024', + environmentColor, environmentColor, mywidth / 2.5), + buildCard(context, 'Livestock', '120mm', '2024', + environmentColor, environmentColor, mywidth / 2.5), ]), - buildCategoryTitle('Environment',environmentColor), + buildCategoryTitle('Environment', environmentColor), buildCardRow([ - buildCard(context,'Climate - Max Temp', '15%', '2024',environmentColor,environmentColor,mywidth/2.5), - buildCard(context,'Climate - Min Temp', '30%', '2023',environmentColor,environmentColor,mywidth/2.5), + buildCard(context, 'Climate - Max Temp', '15%', '2024', + environmentColor, environmentColor, mywidth / 2.5), + buildCard(context, 'Climate - Min Temp', '30%', '2023', + environmentColor, environmentColor, mywidth / 2.5), ]), buildCardRow([ - buildCard(context,'Desalinated Produced Water', '15%', '2024',environmentColor,environmentColor,mywidth/2.5), - buildCard(context,'Area of Natural Reserves', '30%', '2023',environmentColor,environmentColor,mywidth/2.5), + buildCard(context, 'Desalinated Produced Water', '15%', '2024', + environmentColor, environmentColor, mywidth / 2.5), + buildCard(context, 'Area of Natural Reserves', '30%', '2023', + environmentColor, environmentColor, mywidth / 2.5), ]), - buildCategoryTitle('Energy',environmentColor), + buildCategoryTitle('Energy', environmentColor), buildCardRow([ - buildCard(context,'Electricity Production', '15%', '2024',environmentColor,environmentColor,mywidth/2.5), - buildCard(context,'Renewable Energy Production', '30%', '2023',environmentColor,environmentColor,mywidth/2.5), + buildCard(context, 'Electricity Production', '15%', '2024', + environmentColor, environmentColor, mywidth / 2.5), + buildCard(context, 'Renewable Energy Production', '30%', '2023', + environmentColor, environmentColor, mywidth / 2.5), ]), ], ), @@ -128,7 +183,7 @@ class uaenumberWidget extends StatelessWidget { ); } - Widget buildCategoryTitle(String title,Color color) { + Widget buildCategoryTitle(String title, Color color) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( @@ -156,7 +211,8 @@ class uaenumberWidget extends StatelessWidget { ); } - Widget buildCard(BuildContext context,String title, String value, String subtitle,Color bordercolor,boldColor,double mywidth) { + Widget buildCard(BuildContext context, String title, String value, + String subtitle, Color bordercolor, boldColor, double mywidth) { // double myheight = MediaQuery.of(context).size.height; // double mywidth = MediaQuery.of(context).size.width; return Card( @@ -181,11 +237,7 @@ class uaenumberWidget extends StatelessWidget { style: robotoRegular11, ), SizedBox(height: 8), - Text( - subtitle, - textAlign: TextAlign.center, - style: subtitleStyle - ), + Text(subtitle, textAlign: TextAlign.center, style: subtitleStyle), SizedBox(height: 8), Text( value, @@ -236,8 +288,7 @@ class _CustomExpandableTileState extends State { child: Container( decoration: BoxDecoration( color: widget.titleBackgroundColor, - borderRadius: BorderRadius.all(Radius.circular(12)) - ), + borderRadius: BorderRadius.all(Radius.circular(12))), padding: const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -268,18 +319,17 @@ class _CustomExpandableTileState extends State { height: isExpanded ? myheight : 0, child: isExpanded ? SingleChildScrollView( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(20)) - ), - - padding: const EdgeInsets.all(10), - child: Column( - children: widget.children, - ), - ), - ) + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.all(Radius.circular(20))), + padding: const EdgeInsets.all(10), + child: Column( + children: widget.children, + ), + ), + ) : null, ), ), @@ -288,5 +338,3 @@ class _CustomExpandableTileState extends State { ); } } - - diff --git a/lib/presentation/Screens/charts/chart.dart b/lib/presentation/Screens/charts/chart.dart index 1911ddee..a34bf4a4 100644 --- a/lib/presentation/Screens/charts/chart.dart +++ b/lib/presentation/Screens/charts/chart.dart @@ -1035,191 +1035,177 @@ class ChartData { // [ // { -// chart_type :"scatter" -// dataset :"divorces" -// is_chart :"true" -// kpi:"divorces" -// filters:[ -// { -// "filter_key": "REF_AREA", -// "filter_data": [ -// "AE", -// "AE-AJ", -// "AE-RK", -// "AE-FJ", -// "AE-AZ", -// "AE-DU", -// "AE-SH", -// "AE-UQ" -// ] -// }, -// { -// "filter_key": "TIME_PERIOD", -// "filter_data": [ -// "2021", -// "2017", -// "2018", -// "2019", -// "2020" -// ] -// }, -// { -// "filter_key": "DV_TYPE", -// "filter_data": [ -// "D_EM_NEF", -// "D_EM_EF", -// "D_NEW_NEF", -// "D_TOT", -// "D_NEM_EF" -// ] -// } -// ] -// group_by:"DV_TYPE" -// main_id :"social" +// chart_heading :"Guest Nights by Region" +// chart_type :"fl_stacked_bar" +// dataset :"hotels" +// group_by :"GUEST_REGION" +// is_chart :"true" +// kpi :"hotel_occupancy_rate_chart_1" +// main_id :"economy" // response:[ // { // "ObsKey": { -// "DV_TYPE": "D_TOT", // "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE-DU", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2017", +// "GUEST_REGION": "AF", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2016", // "UNIT_MEASURE": "NUMBER" // }, // "ObsValue": { -// "Value": "1484" +// "Value": "2659446" // } // }, // { // "ObsKey": { -// "DV_TYPE": "D_TOT", // "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE-DU", -// "SOURCE_DETAIL": "FCSAMD", +// "GUEST_REGION": "AF", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2017", +// "UNIT_MEASURE": "NUMBER" +// }, +// "ObsValue": { +// "Value": "2721363" +// } +// }, +// { +// "ObsKey": { +// "FREQ": "A", +// "GUEST_REGION": "ASC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2016", +// "UNIT_MEASURE": "NUMBER" +// }, +// "ObsValue": { +// "Value": "16454601" +// } +// }, +// { +// "ObsKey": { +// "FREQ": "A", +// "GUEST_REGION": "ASC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2017", +// "UNIT_MEASURE": "NUMBER" +// }, +// "ObsValue": { +// "Value": "18960773" +// } +// }, +// { +// "ObsKey": { +// "FREQ": "A", +// "GUEST_REGION": "UAE", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", // "TIME_PERIOD": "2019", // "UNIT_MEASURE": "NUMBER" // }, // "ObsValue": { -// "Value": "1308" +// "Value": "14286573" // } // }, // { // "ObsKey": { -// "DV_TYPE": "D_TOT", // "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE-DU", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "1484" -// } -// }, -// { -// "ObsKey": { -// "DV_TYPE": "D_TOT", -// "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE-DU", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2018", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "1321" -// } -// }, -// { -// "ObsKey": { -// "DV_TYPE": "D_NEM_EF", -// "FREQ": "A", -// "MEASURE": "D", +// "GUEST_REGION": "UAE", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", // "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "181" -// } -// }, -// { -// "ObsKey": { -// "DV_TYPE": "D_NEM_EF", -// "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2019", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "182" -// } -// }, -// { -// "ObsKey": { -// "DV_TYPE": "D_EM_NEF", -// "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "766" -// } -// }, -// { -// "ObsKey": { -// "DV_TYPE": "D_EM_NEF", -// "FREQ": "A", -// "MEASURE": "D", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", +// "SOURCE_DETAIL": "FCSC", // "TIME_PERIOD": "2020", // "UNIT_MEASURE": "NUMBER" // }, // "ObsValue": { -// "Value": "618" +// "Value": "13982919" // } // }, // { // "ObsKey": { -// "DV_TYPE": "D_EM_EF", // "FREQ": "A", -// "MEASURE": "D", +// "GUEST_REGION": "EC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", // "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2019", +// "UNIT_MEASURE": "NUMBER" +// }, +// "ObsValue": { +// "Value": "19917654" +// } +// }, +// { +// "ObsKey": { +// "FREQ": "A", +// "GUEST_REGION": "EC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", +// "TIME_PERIOD": "2020", +// "UNIT_MEASURE": "NUMBER" +// }, +// "ObsValue": { +// "Value": "10041664" +// } +// }, +// { +// "ObsKey": { +// "FREQ": "A", +// "GUEST_REGION": "OC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", +// "REF_AREA": "AE", +// "SOURCE_DETAIL": "FCSC", // "TIME_PERIOD": "2017", // "UNIT_MEASURE": "NUMBER" // }, // "ObsValue": { -// "Value": "1717" +// "Value": "1260359" // } // }, // { // "ObsKey": { -// "DV_TYPE": "D_EM_EF", // "FREQ": "A", -// "MEASURE": "D", +// "GUEST_REGION": "OC", +// "H_INDICATOR": "GUN", +// "H_TYPE": "_Z", +// "MEASURE": "H", // "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSAMD", +// "SOURCE_DETAIL": "FCSC", // "TIME_PERIOD": "2018", // "UNIT_MEASURE": "NUMBER" // }, // "ObsValue": { -// "Value": "1720" +// "Value": "1127282" // } // } // // ] -// sub_id :"vital_statistics" -// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_DV_NA,1.5.0/.A....?startPeriod=2017&endPeriod=2024&dimensionAtObservation=AllDimensions" +// sub_id :"tourism" +// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_GUEST_REGION,4.3.0/...A..GUN.OTH+OC+AF+EC+AM+AC+ASC+GCC+UAE.?startPeriod=2016&dimensionAtObservation=AllDimensions" // } // ] diff --git a/lib/presentation/Screens/charts/filters/search_filter_helper.dart b/lib/presentation/Screens/charts/filters/search_filter_helper.dart new file mode 100644 index 00000000..afaa9f75 --- /dev/null +++ b/lib/presentation/Screens/charts/filters/search_filter_helper.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; + +List filterCharts({ + required List apiData, + required Map> filters, +}) { +// Initialize the filtered data as the original dataset + List filteredData = apiData; + +// Apply filtering for each filter key dynamically + for (var filterKey in filters.keys) { + List? filterValues = filters[filterKey]; + if (filterValues != null && filterValues.isNotEmpty) { + // Filter data based on the current filter key + List currentFilteredData = filteredData.where((item) { + var obsKey = item["ObsKey"]; + return filterValues.contains(obsKey[filterKey]); + }).toList(); + + // If current filter excludes all items, return previous filtered data + if (currentFilteredData.isEmpty) { + return filteredData; + } + + // Update filtered data for the next iteration + filteredData = currentFilteredData; + } + } + print('filteredData $filteredData'); + return filteredData; // Return the final filtered data +} diff --git a/lib/presentation/Screens/charts/models/chart_data.dart b/lib/presentation/Screens/charts/models/chart_data.dart new file mode 100644 index 00000000..227d11fc --- /dev/null +++ b/lib/presentation/Screens/charts/models/chart_data.dart @@ -0,0 +1,13 @@ +class ChartData { + final String xLabel; + final double yValue; + + ChartData({required this.xLabel, required this.yValue}); + + factory ChartData.fromJson(Map json) { + return ChartData( + xLabel: json['x_label'] as String, + yValue: (json['y_value'] as num).toDouble(), + ); + } +} diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart new file mode 100644 index 00000000..0237b800 --- /dev/null +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -0,0 +1,600 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:uae_stat/presentation/Screens/charts/services/api_service.dart'; +import 'package:uae_stat/presentation/Screens/charts/widgets/chart_widget.dart'; + +import '../filters/search_filter_helper.dart'; + +class ChartScreen1 extends StatefulWidget { + final String dataSets; + const ChartScreen1({Key? key, required this.dataSets}); + @override + _ChartScreen1State createState() => _ChartScreen1State(); +} + +class _ChartScreen1State extends State { + List> selectedFiltersStorage = []; + final GlobalKey _scaffoldKey = GlobalKey(); + final ApiService apiService = ApiService(); + bool isLoading = true; + List isChartData = []; + List nonChartData = []; + List filterData = []; + List chartsData = []; + List cardData = []; + List originalChartsData = []; + List originalCardData = []; + + @override + void initState() { + super.initState(); + fetchChartData(widget.dataSets); + } + + Future fetchChartData(String dataSets) async { + var data = await apiService.fetchChartData(dataSets); + setState(() { + isChartData = data['isChartData'] ?? []; + nonChartData = data['nonChartData'] ?? []; + filterData = data['filterData'] ?? []; + + originalChartsData = List.from(isChartData); // Store original data + originalCardData = List.from(nonChartData); + + chartsData = isChartData; + cardData = nonChartData; + print('chartsData :- $chartsData'); + print('cardData :- $cardData'); + print('filterData :- $filterData'); + isLoading = false; + }); + print(chartsData); + print(cardData); + } + + void applyFilters(BuildContext context, List filters, List data, + List dataCard, List selectedFilters) { + print("Selected Filters before applying: $selectedFilters"); + // Check if all filter_data is empty + if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) { + setState(() { + chartsData = List.from(originalChartsData); // Restore the original data + cardData = List.from(originalCardData); // Restore the original data + }); + print('Returning as no filters are selected'); + Navigator.pop(context); + return; // Exit early since all filter_data are empty + } + + // Loop through each chart data in the `data` list + List filteredData = []; + for (var chart in data) { + // Extract response data for filtering + List response = chart['response'] ?? []; + + // Filter the response based on selected filters + var chartFilteredData = response.where((responseItem) { + final obsKey = responseItem['ObsKey']; + + // Check if each selected filter's `filter_data` matches `ObsKey` values + return selectedFilters.every((filter) { + final filterKey = filter['filter_key']; + final filterValues = filter['filter_data']; + + // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data` + if (obsKey.containsKey(filterKey)) { + final obsKeyValue = obsKey[filterKey]?.toString(); + return filterValues.isEmpty || filterValues.contains(obsKeyValue); + } + return false; + }); + }).toList(); + + // If any data matches the filter, add the whole chart data object + if (chartFilteredData.isNotEmpty) { + filteredData.add({ + ...chart, // Include all other properties of the chart object + 'response': chartFilteredData, // Only include filtered response data + }); + } + } + + List filteredCardData = []; + for (var chart in dataCard) { + Map chartData = Map.from(chart); + // Extract response data for filtering + List response = chartData['response'] ?? []; + + // Filter the response based on selected filters + var cardFilteredData = response.where((responseItem) { + final obsKey = responseItem['ObsKey']; + + // Check if each selected filter's `filter_data` matches `ObsKey` values + return selectedFilters.every((filter) { + final filterKey = filter['filter_key']; + final filterValues = filter['filter_data']; + + // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data` + if (obsKey.containsKey(filterKey)) { + final obsKeyValue = obsKey[filterKey]?.toString(); + return filterValues.isEmpty || filterValues.contains(obsKeyValue); + } + return false; + }); + }).toList(); + + // If any data matches the filter, add the whole chart data object + if (cardFilteredData.isNotEmpty) { + filteredCardData.add({ + ...chart, // Include all other properties of the chart object + 'response': cardFilteredData, // Only include filtered response data + }); + } + } + + // Update the chartsData with the filtered data + setState(() { + chartsData = filteredData; + cardData = filteredCardData; + }); + + print("Filtered Data: $filteredData"); + print("Filtered Card: $filteredCardData"); + + // Go back after applying filters + Navigator.pop(context); + } + + void showRightSideModal(BuildContext context, List filters, List data) { + // Initialize selected filters structure from the stored value, if exists + List> selectedFilters = + selectedFiltersStorage.isNotEmpty + ? List.from(selectedFiltersStorage) // Use stored filters + : filters.map((filter) { + return {"filter_key": filter["filter_key"], "filter_data": []}; + }).toList(); // Or initialize empty filters + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return Align( + alignment: Alignment.centerRight, + child: Container( + width: MediaQuery.of(context).size.width * 0.7, + height: MediaQuery.of(context).size.height, + color: Colors.white, + child: Column( + children: [ + // Header Section + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Filters', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + IconButton( + icon: Icon(Icons.close), + onPressed: () => Navigator.pop(context), + ), + ], + ), + ), + // Filters Section + Expanded( + child: ListView.builder( + itemCount: filters.length, + itemBuilder: (context, index) { + final filter = filters[index]; + final filterKey = filter["filter_key"]; + final filterData = filter["filter_data"]; + + return StatefulBuilder( + builder: (context, setState) { + // Get the corresponding selected filter object + var selectedFilter = selectedFilters + .firstWhere((f) => f["filter_key"] == filterKey); + + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + filterKey, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return StatefulBuilder( + builder: (context, dialogSetState) { + return AlertDialog( + title: Text('Select $filterKey'), + content: SingleChildScrollView( + child: ListBody( + children: filterData + .map((value) { + return CheckboxListTile( + title: Text( + value.toString()), + value: selectedFilter[ + "filter_data"] + .contains(value), + onChanged: + (bool? isChecked) { + dialogSetState(() { + if (isChecked == + true) { + selectedFilter[ + "filter_data"] + .add(value); + } else { + selectedFilter[ + "filter_data"] + .remove(value); + } + }); + }, + ); + }).toList(), + ), + ), + actions: [ + TextButton( + onPressed: () { + // Store the updated selected filters after dialog closes + setState(() {}); + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + ); + }, + ); + }, + ); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric( + horizontal: 16.0, vertical: 12.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(8.0), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 4.0, + children: + selectedFilter["filter_data"].isEmpty + ? [ + Text('Select $filterKey', + style: TextStyle( + color: Colors.grey)) + ] + : selectedFilter["filter_data"] + .map((value) { + return Chip( + label: Text(value), + onDeleted: () { + setState(() { + selectedFilter[ + "filter_data"] + .remove(value); + }); + }, + ); + }).toList(), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ), + ), + // Button Section + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ElevatedButton( + onPressed: () { + // Clear all selected filters + selectedFilters.forEach((filter) { + filter["filter_data"].clear(); + }); + setState(() { + chartsData = List.from(originalChartsData); + cardData = List.from(originalCardData); + }); + // Reset the selectedFiltersStorage to empty when clearing + selectedFiltersStorage.clear(); + Navigator.pop(context); + }, + child: Text('Clear'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey, + ), + ), + ElevatedButton( + onPressed: () { + print( + "Selected Filters before applying: $selectedFilters"); + setState(() { + chartsData = originalChartsData; + cardData = originalCardData; + }); + + applyFilters(context, filters, chartsData, cardData, + selectedFilters); + // Save the selected filters to storage after applying + selectedFiltersStorage = List.from(selectedFilters); + }, + child: Text('Apply Filter'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + backgroundColor: Colors.brown[200]!, + appBar: AppBar( + backgroundColor: Colors.brown[200]!, + elevation: 0, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), + onPressed: () { + context.go('/myhomepage'); + }, + ), + title: Text( + 'UAE Numbers', + style: TextStyle(color: Colors.white), + ), + ), + body: isLoading + ? Center(child: CircularProgressIndicator()) + : Column( + children: [ + Expanded( + child: ListView( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment + .end, // Aligns the row's content to the right + children: [ + Row( + children: [ + Text( + 'Bookmark', + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + Icon(Icons.bookmark_add_outlined, + color: Colors.white, size: 18), + ], + ), + SizedBox( + width: 10), // Horizontal space between items + Row( + children: [ + Text( + 'Share', + style: const TextStyle( + fontSize: 16, + color: Colors.white, + ), + ), + Icon(Icons.share_sharp, + color: Colors.white, size: 18), + ], + ), + SizedBox( + width: 10), // Horizontal space between items + IconButton( + icon: Icon(Icons.filter_alt_outlined, + color: Colors.white, size: 18), + onPressed: () { + showRightSideModal( + context, filterData, chartsData); + }, + ), + ], + ), + ], + ), + // nonChartData Cards + GridView.builder( + itemCount: cardData.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // 2 cards per row + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 177.5 / + 180, // Adjusted to maintain width and fixed height + ), + itemBuilder: (context, index) { + final item = cardData[index]; + print('item item item $item'); + final chart_type = item['chart_type']; + final chart_heading = item['chart_heading']; + final data = apiService.processNonChartData(item); + print('processNonChartData'); + + if (chart_type == 'total') { + return Card( + margin: const EdgeInsets.all(10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.public, + color: Color(0xFF90B0D5), size: 22), + const SizedBox(height: 5), + Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + Text( + '(${data['lastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 10, color: Colors.grey), + ), + Text( + '${data['lastYearValue'] ?? 'NA'}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF90B0D5), + ), + ), + const Divider( + color: Colors.grey, thickness: 1), + Text( + '${data['secondLastYearValue'] ?? 'NA'}', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Color(0xFF90B0D5), + ), + ), + Text( + '(${data['secondLastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 10, color: Colors.grey), + ), + ], + ), + ), + ); + } else if (chart_type == 'average') { + return Card( + margin: const EdgeInsets.all(10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.analytics, + color: Color(0xFF90B0D5), size: 22), + const SizedBox(height: 5), + Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + Text( + '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 10, color: Colors.grey), + ), + Text( + '${data['roundedAverage'] ?? 'NA'}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF90B0D5), + ), + ), + ], + ), + ), + ); + } else { + return const SizedBox + .shrink(); // Ignore unknown KPIs + } + }, + ), + // chartsData Cards + ListView.builder( + itemCount: chartsData.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.all(10), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + Container( + height: 300, + // child: buildChart(chartsData[index]), + child: ChartWidget( + chartData: chartsData[index])), + ], + ), + ), + ); + }, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/Screens/charts/services/api_service.dart b/lib/presentation/Screens/charts/services/api_service.dart new file mode 100644 index 00000000..02952e20 --- /dev/null +++ b/lib/presentation/Screens/charts/services/api_service.dart @@ -0,0 +1,184 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'dart:collection'; + +class ChartData { + final DateTime? xDateTime; + final String? x; // Optional for non-DateTime-based charts + final double y; + + ChartData({this.xDateTime, this.x, required this.y}); +} + +class ApiService { + static const String baseUrl = 'https://pb.venbait.in/api/getDataSet'; + + Future> fetchChartData(String dataSets) async { + List isChartData = []; + List nonChartData = []; + List originalChartsData = []; + List originalCardData = []; + + final url = Uri.parse('$baseUrl?dataset=$dataSets'); + + try { + final response = await http.get(url); + print('data'); + if (response.statusCode == 200) { + final Map jsonData = jsonDecode(response.body); + // Variables to store filtered data + for (var item in jsonData['data']) { + if (item['is_chart'] == 'true') { + isChartData.add(item); + } else if (item['is_chart'] == 'false') { + nonChartData.add(item); + } + } + + // Save original data for reset + // originalChartsData = List.from(isChartData); // Store original data + // originalCardData = List.from(nonChartData); // Store original data + + // Return the data as a Map + return { + 'isChartData': isChartData, + 'nonChartData': nonChartData, + 'filterData': jsonData['filter_data'] + // 'originalChartsData': originalChartsData, + // 'originalCardData': originalCardData, + }; + } else { + throw Exception('Failed to load data'); + } + } catch (error) { + print('Error fetching data: $error'); + return {}; + } + } + + /// Fetch chart data and categorize it into chart and non-chart data + // Future fetchChartData(String dataSets) async { + // final url = Uri.parse('$baseUrl?dataset=$dataSets'); + // + // List isChartData = []; + // List nonChartData = []; + // List originalChartsData = []; + // List originalCardData = []; + // + // try { + // final response = await http.get(url); + // + // if (response.statusCode == 200) { + // final List data = jsonDecode(response.body); + // + // for (var item in data) { + // if (item['is_chart'] == 'true') { + // isChartData.add(item); + // } else if (item['is_chart'] == 'false') { + // nonChartData.add(item); + // } + // } + // + // print('Chart Data: $isChartData'); + // print('Non-Chart Data: $nonChartData'); + // + // originalChartsData = List.from(isChartData); + // originalCardData = List.from(nonChartData); + // + // print('chartsData $isChartData'); + // print('cardData $nonChartData'); + // } else { + // throw Exception('Failed to load data'); + // } + // } catch (error) { + // print('Error fetching data: $error'); + // } + // } + + /// Process non-chart data by extracting and calculating values + Map processNonChartData(dynamic item) { + print('Processing non-chart data...'); + + try { + final response = item['response']; + + final List responseData = response is LinkedHashMap + ? Map.from(response) + : response; + + final sortedResponse = List.from( + responseData.map((e) => e['ObsKey']['TIME_PERIOD'].toString()), + )..sort((a, b) => b.compareTo(a)); + + String? lastYear = + sortedResponse.isNotEmpty ? sortedResponse.first : null; + String? secondLastYear = + sortedResponse.length > 1 ? sortedResponse[1] : null; + String? firstYear = + sortedResponse.isNotEmpty ? sortedResponse.last : null; + + String? lastYearValue = response.firstWhere( + (item) => item['ObsKey']['TIME_PERIOD'].toString() == lastYear, + orElse: () => null, + )?['ObsValue']['Value']; + + String? secondLastYearValue = response.firstWhere( + (item) => item['ObsKey']['TIME_PERIOD'].toString() == secondLastYear, + orElse: () => null, + )?['ObsValue']['Value']; + + List allValues = responseData + .map((item) => + double.tryParse(item['ObsValue']['Value'].toString()) ?? 0.0) + .toList(); + + double sum = allValues.reduce((a, b) => a + b); + double average = sum / allValues.length; + double roundedAverage = average.roundToDouble(); + + return { + 'lastYear': lastYear ?? 'NA', + 'secondLastYear': secondLastYear ?? 'NA', + 'firstYear': firstYear ?? 'NA', + 'lastYearValue': lastYearValue ?? 'NA', + 'secondLastYearValue': secondLastYearValue ?? 'NA', + 'roundedAverage': roundedAverage, + 'allValues': allValues.isEmpty ? [0.0] : allValues, + }; + } catch (e) { + print('Error processing data: $e'); + return {}; + } + } + + /// Parse scatter chart data + List parseScatterChartData(List 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(); + } + + /// Parse stacked bar chart data + List parseStackedBarChartData( + dynamic chartData, String groupByKey, String groupByValue) { + return chartData['response'] + .where((entry) => entry['ObsKey'][groupByKey] == groupByValue) + .map((entry) { + return ChartData( + x: entry['ObsKey']['TIME_PERIOD'], + y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0, + ); + }).toList(); + } +} diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart new file mode 100644 index 00000000..4e7c4836 --- /dev/null +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -0,0 +1,632 @@ +import 'dart:math'; + +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; + +class ChartWidget extends StatelessWidget { + final dynamic chartData; + + ChartWidget({Key? key, required this.chartData}) : super(key: key); + + String capitalizeAndSplit(String input) { + return input + .split('_') + .map((word) => word[0].toUpperCase() + word.substring(1)) + .join(' '); + } + + List parseStackedBarChartData( + dynamic chartData, String groupByKey, String groupByValue) { + return chartData['response'] + .where((entry) => entry['ObsKey'][groupByKey] == groupByValue) + .map((entry) { + return ChartData( + x: entry['ObsKey']['TIME_PERIOD'], + y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0, + ); + }).toList(); + } + + List parseColumnChartData(dynamic chartData) { + return chartData['response'].map((entry) { + return BarChartGroupData( + x: int.tryParse(entry['ObsKey']['TIME_PERIOD']) ?? 0, + barRods: [ + BarChartRodData( + toY: double.tryParse(entry['ObsValue']['Value']) ?? 0.0, + color: Colors.blue, + width: 16, + borderRadius: BorderRadius.zero, + ), + ], + ); + }).toList(); + } + + List parsePieChartData( + dynamic chartData, + double totalValue, + int? touchedIndex, + ) { + return chartData['response'] + .asMap() + .entries + .map((entry) { + int index = entry.key; + var data = entry.value; + double value = double.tryParse(data['ObsValue']['Value']) ?? 0.0; + double percentage = (value / totalValue) * 100; + bool isTouched = index == touchedIndex; + return PieChartSectionData( + value: value, + color: Colors.primaries[index % Colors.primaries.length], + title: '${percentage.toStringAsFixed(1)}%', + radius: isTouched ? 60 : 50, // Increase size when touched + titleStyle: TextStyle( + fontSize: isTouched ? 12 : 10, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ); + }).toList(); + } + + List generateIndicators(dynamic chartData, groupByValue) { + return chartData['response'].asMap().entries.map((entry) { + int index = entry.key; + var data = entry.value; + Color color = Colors.primaries[index % Colors.primaries.length]; + String title = data['ObsKey'][groupByValue] ?? ''; + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 16, + height: 16, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8), + Text( + title, + style: TextStyle(fontSize: 14), + ), + ], + ); + }).toList(); + } + + Set extractGroupByValues(dynamic chartData, String groupByKey) { + return (chartData['response'] as List).map((entry) { + var value = entry['ObsKey'][groupByKey]; + return value != null + ? value.toString() + : ''; // Handle null and ensure conversion to String + }).toSet(); + } + + Widget buildChart(dynamic chartData) { + print('chartDataccccccc $chartData'); + // Extract group_by dynamically from the chartData + if (chartData == null || chartData['response'] == null) { + return Center(child: Text('No chart data available')); + } + + String groupByKey = chartData['group_by'] ?? ''; + print('groupByKey $groupByKey'); + Set groupByValues = extractGroupByValues(chartData, groupByKey); + print('groupByValues $groupByValues'); + + switch (chartData['chart_type']) { + case 'stacked_bar': + String chartTitle = + capitalizeAndSplit(chartData['chart_heading'] ?? ''); + // Define a list of colors + final List uniqueColors = [ + Color(0xFF648CBA), + Color(0xFF90B0D5), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + return SfCartesianChart( + primaryXAxis: CategoryAxis(), + title: ChartTitle(text: chartData['chart_heading']), + legend: Legend( + isVisible: true, + position: LegendPosition.bottom, + overflowMode: LegendItemOverflowMode.scroll, + ), + tooltipBehavior: TooltipBehavior( + enable: true, // Enable tooltips + format: 'point.x : point.y', // Custom tooltip format + ), + series: >[ + for (int i = 0; i < groupByValues.length; i++) + StackedBarSeries( + dataSource: parseStackedBarChartData( + chartData, groupByKey, groupByValues.elementAt(i)), + xValueMapper: (ChartData data, _) => data.x ?? '', + yValueMapper: (ChartData data, _) => data.y, + name: groupByValues.elementAt(i), + color: uniqueColors[i % uniqueColors.length], + enableTooltip: true, + ), + ], + ); + case 'stacked_column': + String chartTitle = + capitalizeAndSplit(chartData['chart_heading'] ?? ''); + // Define a list of colors + final List uniqueColors = [ + Color(0xFF648CBA), + Color(0xFF90B0D5), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + return SfCartesianChart( + primaryXAxis: CategoryAxis(), + title: ChartTitle(text: chartData['chart_heading']), + legend: Legend( + isVisible: true, + position: LegendPosition.bottom, + overflowMode: LegendItemOverflowMode.scroll, + ), + tooltipBehavior: TooltipBehavior( + enable: true, // Enable tooltips + format: 'point.x : point.y', // Custom tooltip format + ), + series: >[ + for (int i = 0; i < groupByValues.length; i++) + StackedColumnSeries( + dataSource: parseStackedBarChartData( + chartData, groupByKey, groupByValues.elementAt(i)), + xValueMapper: (ChartData data, _) => data.x ?? '', + yValueMapper: (ChartData data, _) => data.y, + name: groupByValues.elementAt(i), + color: uniqueColors[i % uniqueColors.length], + enableTooltip: true, + ), + ], + ); + case 'column_chart': // Case 3 for fl_chart column chart + return BarChart( + BarChartData( + gridData: FlGridData(show: false), + titlesData: FlTitlesData(show: true), + borderData: FlBorderData(show: false), + barGroups: parseColumnChartData(chartData), + ), + ); + case 'pie_chart': + double totalValue = chartData['response'] + .map( + (entry) => double.tryParse(entry['ObsValue']['Value']) ?? 0.0) + .fold(0.0, (prev, element) => prev + element); + + ValueNotifier touchedIndex = ValueNotifier(null); + + return Column( + children: [ + Text( + chartData['chart_heading'] ?? + 'Pie Chart', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 20), + Expanded( + child: ValueListenableBuilder( + valueListenable: touchedIndex, + builder: (context, value, child) { + return PieChart( + PieChartData( + sections: parsePieChartData( + chartData, totalValue, touchedIndex.value), + borderData: FlBorderData(show: false), + sectionsSpace: 2, + centerSpaceRadius: 50, + pieTouchData: PieTouchData( + touchCallback: (FlTouchEvent event, pieTouchResponse) { + if (pieTouchResponse?.touchedSection != null && + event is! FlTapUpEvent) { + touchedIndex.value = pieTouchResponse! + .touchedSection!.touchedSectionIndex; + } else { + touchedIndex.value = + null; // Reset when not touching + } + }, + ), + ), + ); + }, + ), + ), + SizedBox(height: 20), + Padding( + padding: + const EdgeInsets.all(8.0), // Add spacing around indicators + child: Wrap( + spacing: 12, + runSpacing: 8, + children: generateIndicators(chartData, chartData['group_by']), + ), + ), + ], + ); + case 'fl_stacked_bar': + // Divide groupByValuesList into chunks of 5 items per row + List groupByValuesList = groupByValues.toList(); + int chunkSize = 5; + List> chunkedGroupByValues = []; + for (int i = 0; i < groupByValuesList.length; i += chunkSize) { + chunkedGroupByValues.add( + groupByValuesList.sublist( + i, + i + chunkSize > groupByValuesList.length + ? groupByValuesList.length + : i + chunkSize), + ); + } + return Column( + children: [ + Text( + chartData['chart_heading'] ?? + 'Pie Chart', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 10), + AspectRatio( + aspectRatio: 1.5, + child: BarChart( + BarChartData( + alignment: BarChartAlignment.spaceBetween, + barTouchData: BarTouchData( + touchTooltipData: BarTouchTooltipData( + getTooltipItem: + (groupData, groupIndex, rodData, rodIndex) { + // print('groupData $groupData'); + // print('groupIndex $groupIndex'); + // print('rodData $rodData'); + // print('rodIndex $rodIndex'); + // Assume groupedData is passed as a parameter + // List timePeriods = + // groupedData.values.first.keys.toList(); + // String timePeriod = timePeriods[groupIndex]; + // List rodStackItems = + // rodData.rodStackItems; + // + // String tooltipText = ''; + // for (int i = 0; i < rodStackItems.length; i++) { + // double fromY = rodStackItems[i].fromY; + // double toY = rodStackItems[i].toY; + // double value = toY - fromY; + // String groupName = groupedData.keys.elementAt(i); + // + // tooltipText += + // '$timePeriod - ${value.toStringAsFixed(0)} - $groupName\n'; + // } + // + // return BarTooltipItem( + // tooltipText, + // TextStyle(color: Colors.white, fontSize: 12), + // ); + }, + ), + // touchCallback: (BarTouchResponse touchResponse) { + // // Handle touch events, such as hover + // if (touchResponse.touchInput is PointerDownEvent) { + // // If you want to handle touch down events, you can add your logic here. + // print('Bar touched: ${touchResponse.touchInput}'); + // } + // }, + handleBuiltInTouches: true, // Enable built-in touch events + ), + titlesData: FlTitlesData( + show: true, + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 28, + getTitlesWidget: (double value, TitleMeta meta) { + return _generateXTitles(chartData)[value.toInt()]; + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 40, + getTitlesWidget: _generateLeftTitles, + ), + ), + topTitles: + AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: + AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + gridData: FlGridData(show: true), + borderData: FlBorderData(show: false), + barGroups: _generateBarGroups(chartData, groupByKey), + ), + ), + ), + // Legend + Padding( + padding: const EdgeInsets.all(5.0), + child: Wrap( + spacing: 5, + runSpacing: 5, + children: chunkedGroupByValues.map((chunk) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: chunk.map((group) { + List groupByValuesList = groupByValues.toList(); + int index = groupByValuesList.indexOf(group); + Color groupColor = _getColorForGroup(index); + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 5.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: groupColor, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 3), + Text( + group, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 11), + ), + ], + ), + ); + }).toList(), + ); + }).toList(), + ), + ), + ], + ); + case 'fl_multi_bar': + return BarChart( + BarChartData( + alignment: BarChartAlignment.spaceAround, + maxY: 100, // Maximum value for the bar + barGroups: _buildBarGroups(), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + '${value.toInt()}%', + style: TextStyle(fontSize: 12), + ); + }, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + "Bar ${value.toInt()}", + style: TextStyle(fontSize: 12), + ); + }, + ), + ), + topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: + AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + borderData: FlBorderData( + show: false, + ), + barTouchData: BarTouchData(enabled: true), + gridData: FlGridData(show: false), + ), + ); + default: + return Center(child: Text('Unknown chart type')); + } + } + + // Define the list of unique colors + final List uniqueColors = [ + Color(0xFF648CBA), + Color(0xFF90B0D5), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + + Color _getColorForGroup(int index) { + // Use the index to get a color from the uniqueColors list + return uniqueColors[index % uniqueColors.length]; + } + + List _generateBarGroups( + dynamic chartData, String groupByKey) { + Map> groupedData = {}; + + // Group data by `group_by` and `TIME_PERIOD`, accumulating the values for each group and year + for (var entry in chartData['response']) { + String groupValue = entry['ObsKey'][groupByKey]; + String timePeriod = entry['ObsKey']['TIME_PERIOD']; + double value = double.tryParse(entry['ObsValue']['Value']) ?? 0.0; + + // print('fl_groupValue $groupValue'); + // print('fl_timePeriod $timePeriod'); + // print('fl_value $value'); + + if (!groupedData.containsKey(groupValue)) { + groupedData[groupValue] = {}; + } + if (!groupedData[groupValue]!.containsKey(timePeriod)) { + groupedData[groupValue]![timePeriod] = 0.0; + } + groupedData[groupValue]![timePeriod] = + groupedData[groupValue]![timePeriod]! + value; + } + + // Create BarChartGroupData for each group + List barGroups = []; + List timePeriods = groupedData.values.first.keys.toList(); + print('timePeriods $timePeriods'); + + // For each time period, generate a BarChartGroupData + for (int i = 0; i < timePeriods.length; i++) { + // Intermediate variable to track the current toY value as we stack bars + double currentToY = 0; + + List rodStackItems = []; + + // For each group (e.g., region), create stacked bars + int groupIndex = 0; // Track group index for color assignment + for (var group in groupedData.keys) { + double value = groupedData[group]![timePeriods[i]] ?? 0.0; + + // Add stacked item with current `toY` value and the incremented one + rodStackItems.add( + BarChartRodStackItem( + currentToY, // The current bottom of the stack + currentToY + value, // The new top of the stack + _getColorForGroup(groupIndex), // Color based on the index + ), + ); + + // Update currentToY after adding the value for this stack + currentToY += value; + + groupIndex++; // Increment group index for the next group + } + + // Create BarChartGroupData with the final stacked value + BarChartGroupData barGroup = BarChartGroupData( + x: i, // Index for the x-axis based on the time period + barRods: [ + BarChartRodData( + toY: currentToY, // Use the accumulated `currentToY` value + rodStackItems: rodStackItems, // Add the stacked items + width: 20, + borderRadius: BorderRadius.zero, + color: Colors + .transparent, // This will act as a container for stacked items + ), + ], + ); + + barGroups.add(barGroup); + } + + return barGroups; + } + + // Function to dynamically generate titles for the x-axis (time periods) + List _generateXTitles(dynamic chartData) { + Set timePeriods = {}; + for (var entry in chartData['response']) { + timePeriods.add(entry['ObsKey']['TIME_PERIOD']); + } + return timePeriods.map((period) { + return Text( + period, + style: TextStyle(fontSize: 12), + ); + }).toList(); + } + +// Function to dynamically set left axis titles (values) + Widget _generateLeftTitles(double value, TitleMeta meta) { + if (value % 1000 == 0) { + return Text(value.toStringAsFixed(0), style: TextStyle(fontSize: 10)); + } + return Container(); + } + + /// Build the data for the bar groups + List _buildBarGroups() { + return [ + BarChartGroupData( + x: 1, // Identifier for the group + barRods: [ + BarChartRodData( + toY: 40, // Value for the first part of the stack + color: Colors.blue, + width: 10, + ), + BarChartRodData( + toY: 60, // Value for the second part of the stack + color: Colors.green, + width: 10, + ), + ], + ), + BarChartGroupData( + x: 2, + barRods: [ + BarChartRodData( + toY: 30, + color: Colors.orange, + width: 10, + ), + BarChartRodData( + toY: 70, + color: Colors.red, + width: 10, + ), + ], + ), + BarChartGroupData( + x: 3, + barRods: [ + BarChartRodData( + toY: 50, + color: Colors.purple, + width: 10, + ), + BarChartRodData( + toY: 50, + color: Colors.cyan, + width: 10, + ), + ], + ), + ]; + } + + @override + Widget build(BuildContext context) { + return buildChart(chartData); + } +} + +class ChartData { + final String? x; + final double y; + final DateTime? xDateTime; + + ChartData({this.x, required this.y, this.xDateTime}); +} diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index 70ed8841..a2a7dcca 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -177,11 +177,11 @@ void handleInfoCardClick(BuildContext context, String data) { final dataSets = data; print(dataSets); if (dataSets == 'hotels') { - context.go('/Chart/$dataSets'); + context.go('/chartScreen/$dataSets'); } else if (dataSets == 'divorces') { - context.go('/Chart/$dataSets'); + context.go('/chartScreen/$dataSets'); } else if (dataSets == 'marriages') { - context.go('/Chart/$dataSets'); + context.go('/chartScreen/$dataSets'); } } diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index cad24495..7138bcba 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -270,7 +270,7 @@ class _BaseScaffoldState extends State { switch (route) { case '/myhomepage': return 0; - case '/about': + case '/uaenumbers': return 1; case '/settings': return 2; @@ -288,7 +288,7 @@ class _BaseScaffoldState extends State { context.go('/myhomepage'); break; case 1: - context.go('/about'); + context.go('/uaenumbers'); break; case 2: context.go('/settings'); diff --git a/pubspec.lock b/pubspec.lock index 3996cee4..8964aeb9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -511,6 +511,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + flutter_staggered_grid_view: + dependency: "direct main" + description: + name: flutter_staggered_grid_view + sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" + url: "https://pub.dev" + source: hosted + version: "0.7.0" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index a1142e8c..d0822126 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,6 +56,7 @@ dependencies: image_picker: ^1.1.2 syncfusion_flutter_charts: 28.1.33 material_charts: ^0.0.23 + flutter_staggered_grid_view: ^0.7.0 dependency_overrides: fading_edge_scrollview: ^4.1.1