cahrt code merged
This commit is contained in:
parent
fb68b1aa79
commit
5ac7afe1f1
@ -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(
|
||||
|
||||
@ -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<CustomExpandableTile> {
|
||||
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<CustomExpandableTile> {
|
||||
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<CustomExpandableTile> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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"
|
||||
// }
|
||||
// ]
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
List<dynamic> filterCharts({
|
||||
required List<dynamic> apiData,
|
||||
required Map<String, List<String>> filters,
|
||||
}) {
|
||||
// Initialize the filtered data as the original dataset
|
||||
List<dynamic> filteredData = apiData;
|
||||
|
||||
// Apply filtering for each filter key dynamically
|
||||
for (var filterKey in filters.keys) {
|
||||
List<String>? filterValues = filters[filterKey];
|
||||
if (filterValues != null && filterValues.isNotEmpty) {
|
||||
// Filter data based on the current filter key
|
||||
List<dynamic> 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
|
||||
}
|
||||
13
lib/presentation/Screens/charts/models/chart_data.dart
Normal file
13
lib/presentation/Screens/charts/models/chart_data.dart
Normal file
@ -0,0 +1,13 @@
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
600
lib/presentation/Screens/charts/screens/chart_screen.dart
Normal file
600
lib/presentation/Screens/charts/screens/chart_screen.dart
Normal file
@ -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<ChartScreen1> {
|
||||
List<Map<String, dynamic>> selectedFiltersStorage = [];
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final ApiService apiService = ApiService();
|
||||
bool isLoading = true;
|
||||
List<dynamic> isChartData = [];
|
||||
List<dynamic> nonChartData = [];
|
||||
List<dynamic> filterData = [];
|
||||
List<dynamic> chartsData = [];
|
||||
List<dynamic> cardData = [];
|
||||
List<dynamic> originalChartsData = [];
|
||||
List<dynamic> originalCardData = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchChartData(widget.dataSets);
|
||||
}
|
||||
|
||||
Future<void> 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<String, dynamic> chartData = Map<String, dynamic>.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<Map<String, dynamic>> 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<Widget>((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<Widget>((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])),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
184
lib/presentation/Screens/charts/services/api_service.dart
Normal file
184
lib/presentation/Screens/charts/services/api_service.dart
Normal file
@ -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<Map<String, dynamic>> fetchChartData(String dataSets) async {
|
||||
List<dynamic> isChartData = [];
|
||||
List<dynamic> nonChartData = [];
|
||||
List<dynamic> originalChartsData = [];
|
||||
List<dynamic> originalCardData = [];
|
||||
|
||||
final url = Uri.parse('$baseUrl?dataset=$dataSets');
|
||||
|
||||
try {
|
||||
final response = await http.get(url);
|
||||
print('data');
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> 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<void> fetchChartData(String dataSets) async {
|
||||
// final url = Uri.parse('$baseUrl?dataset=$dataSets');
|
||||
//
|
||||
// List<dynamic> isChartData = [];
|
||||
// List<dynamic> nonChartData = [];
|
||||
// List<dynamic> originalChartsData = [];
|
||||
// List<dynamic> originalCardData = [];
|
||||
//
|
||||
// try {
|
||||
// final response = await http.get(url);
|
||||
//
|
||||
// if (response.statusCode == 200) {
|
||||
// final List<dynamic> 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<String, dynamic> processNonChartData(dynamic item) {
|
||||
print('Processing non-chart data...');
|
||||
|
||||
try {
|
||||
final response = item['response'];
|
||||
|
||||
final List<dynamic> responseData = response is LinkedHashMap
|
||||
? Map<String, dynamic>.from(response)
|
||||
: response;
|
||||
|
||||
final sortedResponse = List<String>.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<double> 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<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();
|
||||
}
|
||||
|
||||
/// Parse stacked bar chart data
|
||||
List<ChartData> parseStackedBarChartData(
|
||||
dynamic chartData, String groupByKey, String groupByValue) {
|
||||
return chartData['response']
|
||||
.where((entry) => entry['ObsKey'][groupByKey] == groupByValue)
|
||||
.map<ChartData>((entry) {
|
||||
return ChartData(
|
||||
x: entry['ObsKey']['TIME_PERIOD'],
|
||||
y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
632
lib/presentation/Screens/charts/widgets/chart_widget.dart
Normal file
632
lib/presentation/Screens/charts/widgets/chart_widget.dart
Normal file
@ -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<ChartData> parseStackedBarChartData(
|
||||
dynamic chartData, String groupByKey, String groupByValue) {
|
||||
return chartData['response']
|
||||
.where((entry) => entry['ObsKey'][groupByKey] == groupByValue)
|
||||
.map<ChartData>((entry) {
|
||||
return ChartData(
|
||||
x: entry['ObsKey']['TIME_PERIOD'],
|
||||
y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<BarChartGroupData> parseColumnChartData(dynamic chartData) {
|
||||
return chartData['response'].map<BarChartGroupData>((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<PieChartSectionData> parsePieChartData(
|
||||
dynamic chartData,
|
||||
double totalValue,
|
||||
int? touchedIndex,
|
||||
) {
|
||||
return chartData['response']
|
||||
.asMap()
|
||||
.entries
|
||||
.map<PieChartSectionData>((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<Widget> generateIndicators(dynamic chartData, groupByValue) {
|
||||
return chartData['response'].asMap().entries.map<Widget>((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<String> extractGroupByValues(dynamic chartData, String groupByKey) {
|
||||
return (chartData['response'] as List<dynamic>).map<String>((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<String> 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<Color> 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: <StackedBarSeries<ChartData, String>>[
|
||||
for (int i = 0; i < groupByValues.length; i++)
|
||||
StackedBarSeries<ChartData, String>(
|
||||
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<Color> 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: <CartesianSeries<ChartData, String>>[
|
||||
for (int i = 0; i < groupByValues.length; i++)
|
||||
StackedColumnSeries<ChartData, String>(
|
||||
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<double>(
|
||||
(entry) => double.tryParse(entry['ObsValue']['Value']) ?? 0.0)
|
||||
.fold(0.0, (prev, element) => prev + element);
|
||||
|
||||
ValueNotifier<int?> 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<int?>(
|
||||
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<String> groupByValuesList = groupByValues.toList();
|
||||
int chunkSize = 5;
|
||||
List<List<String>> 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<String> timePeriods =
|
||||
// groupedData.values.first.keys.toList();
|
||||
// String timePeriod = timePeriods[groupIndex];
|
||||
// List<BarChartRodStackItem> 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<String> 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<Color> 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<BarChartGroupData> _generateBarGroups(
|
||||
dynamic chartData, String groupByKey) {
|
||||
Map<String, Map<String, double>> 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<BarChartGroupData> barGroups = [];
|
||||
List<String> 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<BarChartRodStackItem> 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<Widget> _generateXTitles(dynamic chartData) {
|
||||
Set<String> 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<BarChartGroupData> _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});
|
||||
}
|
||||
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -270,7 +270,7 @@ class _BaseScaffoldState extends State<BaseScaffold> {
|
||||
switch (route) {
|
||||
case '/myhomepage':
|
||||
return 0;
|
||||
case '/about':
|
||||
case '/uaenumbers':
|
||||
return 1;
|
||||
case '/settings':
|
||||
return 2;
|
||||
@ -288,7 +288,7 @@ class _BaseScaffoldState extends State<BaseScaffold> {
|
||||
context.go('/myhomepage');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/about');
|
||||
context.go('/uaenumbers');
|
||||
break;
|
||||
case 2:
|
||||
context.go('/settings');
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user