user guide
This commit is contained in:
commit
fe8632946c
@ -26,11 +26,11 @@ if (flutterVersionName == null) {
|
||||
android {
|
||||
namespace = "ae.gov.fcsc.frontend"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
// ndkVersion = flutter.ndkVersion
|
||||
ndkVersion = "25.1.8937393"
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.java.home=C:\\Program Files\\Eclipse Adoptium\\jdk-17.0.13.11-hotspot
|
||||
org.gradle.java.home=C:/Program Files/Eclipse Adoptium/jdk-21.0.5.11-hotspot
|
||||
|
||||
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
|
||||
|
||||
@ -18,8 +18,9 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
id "com.android.application" version "8.2.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
@ -294,7 +294,11 @@
|
||||
|
||||
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/auth_verification/registration.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';
|
||||
|
||||
@ -303,6 +307,8 @@ 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';
|
||||
import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
||||
@ -317,8 +323,13 @@ final GoRouter router = GoRouter(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
//builder: (context, state) => LoginRoute(),
|
||||
builder: (context, state) => LoginRoute(),
|
||||
// builder: (context, state) => MyHomePage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/login',
|
||||
builder: (context, state) => LoginRoute(),
|
||||
//builder: (context, state) => LoginRoute(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/myhomepage',
|
||||
@ -328,6 +339,22 @@ final GoRouter router = GoRouter(
|
||||
path: '/register',
|
||||
builder: (context, state) => RegisterScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/DemoHome/:dataSets',
|
||||
builder: (context, state) {
|
||||
final dataSets = state.pathParameters['dataSets']!;
|
||||
print('Router $dataSets');
|
||||
return ChartPage(dataSets: dataSets);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/Chart/:dataSets',
|
||||
builder: (context, state) {
|
||||
final dataSets = state.pathParameters['dataSets']!;
|
||||
print('Router $dataSets');
|
||||
return ChartScreen(dataSets: dataSets);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/mailverification',
|
||||
builder: (context, state) => EmailVerificationScreen(
|
||||
@ -410,5 +437,21 @@ final GoRouter router = GoRouter(
|
||||
path: '/manageuser',
|
||||
builder: (context, state) => ManageUserRouter(),
|
||||
),
|
||||
|
||||
// Bottom Navigation Routes
|
||||
GoRoute(
|
||||
path: '/uaenumbers',
|
||||
builder: (context, state) => UaeNumbers(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: '/competitiveness',
|
||||
builder: (context, state) => Competitiveness(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: '/countryprofile',
|
||||
builder: (context, state) => CountryProfile(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
||||
|
||||
class Competitiveness extends StatelessWidget {
|
||||
const Competitiveness({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: CompetitivenessState(context),
|
||||
);
|
||||
}
|
||||
Widget CompetitivenessState(BuildContext context) {return Center(child: Text("Competitiveness"));}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
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"),);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,292 @@
|
||||
import 'package:flutter/material.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';
|
||||
|
||||
|
||||
class UaeNumbers extends StatelessWidget {
|
||||
const UaeNumbers({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double myheight = MediaQuery.of(context).size.height;
|
||||
double mywidth = MediaQuery.of(context).size.width;
|
||||
return BaseScaffold(
|
||||
title: Text("UAE Numbers"),
|
||||
body:
|
||||
//EconomicDashboard(),
|
||||
//EconomyExpandTile()
|
||||
uaenumberWidget());
|
||||
}
|
||||
}
|
||||
|
||||
class uaenumberWidget extends StatelessWidget {
|
||||
const uaenumberWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double myheight = MediaQuery.of(context).size.height;
|
||||
double mywidth = MediaQuery.of(context).size.width;
|
||||
return Padding(
|
||||
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,),
|
||||
CustomExpandableTile(
|
||||
title: 'ECONOMY',
|
||||
titleBackgroundColor: economyColor,
|
||||
children: [
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
buildCategoryTitle('Prices',economyColor),
|
||||
buildCardRow([
|
||||
buildCard(context,'Total Export', '669.9B', 'Jan - Mar 2024 (AED)',economyColor,economyColor,mywidth/1.2),
|
||||
]),
|
||||
],
|
||||
),
|
||||
CustomExpandableTile(
|
||||
title: 'SOCIAL',
|
||||
titleBackgroundColor: socialColor,
|
||||
children: [
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
],
|
||||
),
|
||||
CustomExpandableTile(
|
||||
title: 'ENVIRONMENT',
|
||||
titleBackgroundColor: environmentColor,
|
||||
children: [
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
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),
|
||||
]),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildCategoryTitle(String title,Color color) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildCardRow(List<Widget> cards) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: cards.map((card) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: card,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
elevation: 2,
|
||||
child: Container(
|
||||
width: mywidth,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // Background color
|
||||
border: Border.all(
|
||||
color: bordercolor, // Border color
|
||||
width: 2, // Border width
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12), // Optional: Rounded corners
|
||||
),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: robotoRegular11,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
subtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: subtitleStyle
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
color: boldColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomExpandableTile extends StatefulWidget {
|
||||
final String title;
|
||||
final Color titleBackgroundColor;
|
||||
final List<Widget> children;
|
||||
|
||||
const CustomExpandableTile({
|
||||
required this.title,
|
||||
required this.titleBackgroundColor,
|
||||
required this.children,
|
||||
});
|
||||
|
||||
@override
|
||||
_CustomExpandableTileState createState() => _CustomExpandableTileState();
|
||||
}
|
||||
|
||||
class _CustomExpandableTileState extends State<CustomExpandableTile> {
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double myheight = MediaQuery.of(context).size.height;
|
||||
return Card(
|
||||
elevation: 4,
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: widget.titleBackgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(12))
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
isExpanded
|
||||
? Icons.keyboard_arrow_up
|
||||
: Icons.keyboard_arrow_down,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
child: AnimatedContainer(
|
||||
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))
|
||||
),
|
||||
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
children: widget.children,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1225
lib/presentation/Screens/charts/chart.dart
Normal file
1225
lib/presentation/Screens/charts/chart.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,12 +13,15 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: ChartPage(),
|
||||
home: ChartPage(dataSets: ''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChartPage extends StatefulWidget {
|
||||
final String dataSets;
|
||||
const ChartPage({Key? key, required this.dataSets});
|
||||
|
||||
@override
|
||||
ChartPageState createState() => ChartPageState();
|
||||
}
|
||||
@ -30,13 +33,13 @@ class ChartPageState extends State<ChartPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchChartData();
|
||||
fetchChartData(widget.dataSets);
|
||||
}
|
||||
|
||||
Future<void> fetchChartData() async {
|
||||
Future<void> fetchChartData(datasets) async {
|
||||
const baseUrl =
|
||||
'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL
|
||||
final url = Uri.parse('$baseUrl?dataset=hotels');
|
||||
final url = Uri.parse('$baseUrl?dataset=$datasets');
|
||||
|
||||
try {
|
||||
final response = await http.get(url);
|
||||
@ -57,24 +60,34 @@ class ChartPageState extends State<ChartPage> {
|
||||
}
|
||||
|
||||
List<ChartData> parseLineChartData(List<dynamic> response) {
|
||||
return response.map((entry) {
|
||||
// Attempt to fetch the TIMEPERIOD
|
||||
final rawTimePeriod =
|
||||
entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD'];
|
||||
final formattedTimePeriod = rawTimePeriod != null
|
||||
? rawTimePeriod.toString() // Use as-is if valid
|
||||
: 'Unknown'; // Fallback value if null
|
||||
// Group by timePeriod (year) and sum the values
|
||||
Map<String, double> groupedData = {};
|
||||
|
||||
// Parse the value or fallback to 0.0
|
||||
response.forEach((entry) {
|
||||
final year = entry['ObsKey']['TIME_PERIOD']?.toString() ?? 'Unknown';
|
||||
final value =
|
||||
double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
|
||||
double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ??
|
||||
0.0;
|
||||
|
||||
return ChartData(
|
||||
timePeriod: formattedTimePeriod,
|
||||
value: value,
|
||||
);
|
||||
}).toList();
|
||||
// Add the value to the existing year (if exists) or initialize it
|
||||
if (groupedData.containsKey(year)) {
|
||||
groupedData[year] = groupedData[year]! + value;
|
||||
} else {
|
||||
groupedData[year] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// Sort the grouped data by year in ascending order
|
||||
var sortedEntries = groupedData.entries.toList()
|
||||
..sort((a, b) => int.parse(a.key).compareTo(int.parse(b.key)));
|
||||
|
||||
// Convert the sorted data to a list of ChartData
|
||||
return sortedEntries
|
||||
.map((entry) => ChartData(
|
||||
timePeriod: entry.key,
|
||||
value: entry.value,
|
||||
value1: 0.0)) // Add value1 as 0.0
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<ChartData> parseBarChartData(List<dynamic> response) {
|
||||
@ -82,63 +95,127 @@ class ChartPageState extends State<ChartPage> {
|
||||
.asMap()
|
||||
.entries
|
||||
.map((entry) => ChartData(
|
||||
timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ??
|
||||
'Unknown', // Fallback to 'Unknown' if null
|
||||
timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? 'Unknown',
|
||||
value: double.tryParse(
|
||||
entry.value['ObsValue']['Value'].toString()) ??
|
||||
0.0, // Handle null/invalid value
|
||||
0.0,
|
||||
value1: 0.0, // Ensure value1 is passed as well
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<ChartData> parseColumnChartData(List<dynamic> response) {
|
||||
List<ChartData> chartDataList = [];
|
||||
|
||||
// Temporary map to store values by TIME_PERIOD
|
||||
Map<String, ChartData> timePeriodMap = {};
|
||||
|
||||
for (var entry in response) {
|
||||
String timePeriod = entry['ObsKey']['TIME_PERIOD'].toString();
|
||||
double value =
|
||||
double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ??
|
||||
0.0;
|
||||
String indicator = entry['ObsKey']['H_INDICATOR'];
|
||||
|
||||
// If the entry is for TOR, map it to 'value'
|
||||
if (indicator == 'TOR') {
|
||||
if (timePeriodMap.containsKey(timePeriod)) {
|
||||
// Update the value by creating a new ChartData object
|
||||
timePeriodMap[timePeriod] = ChartData(
|
||||
timePeriod: timePeriod,
|
||||
value: value, // Set the new value
|
||||
value1: timePeriodMap[timePeriod]?.value1 ?? 0.0,
|
||||
);
|
||||
} else {
|
||||
timePeriodMap[timePeriod] = ChartData(
|
||||
timePeriod: timePeriod,
|
||||
value: value,
|
||||
value1: 0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If the entry is for TAR, map it to 'value1'
|
||||
if (indicator == 'TAR') {
|
||||
if (timePeriodMap.containsKey(timePeriod)) {
|
||||
// Create a new ChartData object with the updated value1
|
||||
timePeriodMap[timePeriod] = ChartData(
|
||||
timePeriod: timePeriod,
|
||||
value: timePeriodMap[timePeriod]?.value ??
|
||||
0.0, // Retain the current value
|
||||
value1: value, // Update the value1
|
||||
);
|
||||
} else {
|
||||
timePeriodMap[timePeriod] = ChartData(
|
||||
timePeriod: timePeriod,
|
||||
value: 0.0,
|
||||
value1: value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the map to a list
|
||||
chartDataList = timePeriodMap.values.toList();
|
||||
|
||||
return chartDataList;
|
||||
}
|
||||
|
||||
Widget buildChart(dynamic chartData) {
|
||||
print('chartData $chartData');
|
||||
switch (chartData['chart_type']) {
|
||||
case 'line':
|
||||
case 'area':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: CategoryAxis(
|
||||
title: AxisTitle(text: 'Year'),
|
||||
labelRotation: 45, // Rotate labels if they overlap
|
||||
),
|
||||
title: ChartTitle(text: 'Hotel Estabhlishments'),
|
||||
legend: Legend(isVisible: true),
|
||||
tooltipBehavior: TooltipBehavior(
|
||||
enable: true,
|
||||
builder: (dynamic data, dynamic point, dynamic series,
|
||||
int pointIndex, int seriesIndex) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Text(
|
||||
'${data.timePeriod}: ${formatNumber(data.value)}',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
series: <CartesianSeries<ChartData, String>>[
|
||||
LineSeries<ChartData, String>(
|
||||
dataSource: parseLineChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => data.timePeriod,
|
||||
yValueMapper: (ChartData data, _) => data.value,
|
||||
name: chartData['dataset'],
|
||||
color: Colors.red,
|
||||
dataLabelSettings: DataLabelSettings(
|
||||
isVisible: true,
|
||||
builder: (dynamic data, dynamic point, dynamic series,
|
||||
int pointIndex, int seriesIndex) {
|
||||
return Text(
|
||||
formatNumber(data.value),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black),
|
||||
);
|
||||
},
|
||||
),
|
||||
primaryXAxis: CategoryAxis(
|
||||
title: AxisTitle(text: 'Year'),
|
||||
labelRotation: 45, // Rotate labels if they overlap
|
||||
),
|
||||
],
|
||||
);
|
||||
title: ChartTitle(text: 'Hotel Estabhlishments'),
|
||||
legend: Legend(isVisible: true),
|
||||
tooltipBehavior: TooltipBehavior(
|
||||
enable: true,
|
||||
builder: (dynamic data, dynamic point, dynamic series,
|
||||
int pointIndex, int seriesIndex) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Text(
|
||||
'${data.timePeriod}: ${formatNumber(data.value)}',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
series: <CartesianSeries>[
|
||||
// AreaSeries for the area chart
|
||||
AreaSeries<ChartData, String>(
|
||||
dataSource: parseLineChartData(
|
||||
chartData['response']), // your chart data
|
||||
color: Color(0xFF7DAFBC), // Area color
|
||||
borderDrawMode: BorderDrawMode
|
||||
.excludeBottom, // Optional, adjust border settings
|
||||
borderColor: Colors.green, // Border color
|
||||
borderWidth: 2, // Border width
|
||||
xValueMapper: (ChartData data, _) =>
|
||||
data.timePeriod, // Map x to DateTime
|
||||
yValueMapper: (ChartData data, _) => data.value,
|
||||
// name: chartData['dataset'],
|
||||
name: 'Hotels',
|
||||
dataLabelSettings: DataLabelSettings(
|
||||
isVisible: true,
|
||||
builder: (dynamic data, dynamic point, dynamic series,
|
||||
int pointIndex, int seriesIndex) {
|
||||
return Text(
|
||||
formatNumber(data.value),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black),
|
||||
);
|
||||
},
|
||||
), // Map y to numerical value
|
||||
)
|
||||
]);
|
||||
case 'bar':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: CategoryAxis(),
|
||||
@ -166,7 +243,7 @@ class ChartPageState extends State<ChartPage> {
|
||||
xValueMapper: (ChartData data, _) => data.timePeriod,
|
||||
yValueMapper: (ChartData data, _) => data.value,
|
||||
name: chartData['dataset'],
|
||||
color: Colors.red,
|
||||
color: Color(0xFF7DAFBC),
|
||||
dataLabelSettings: DataLabelSettings(
|
||||
isVisible: true,
|
||||
builder: (dynamic data, dynamic point, dynamic series,
|
||||
@ -182,6 +259,41 @@ class ChartPageState extends State<ChartPage> {
|
||||
),
|
||||
],
|
||||
);
|
||||
case 'column':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: CategoryAxis(
|
||||
title: AxisTitle(text: 'Year'),
|
||||
labelRotation: 45, // Rotate labels if they overlap
|
||||
),
|
||||
primaryYAxis: NumericAxis(
|
||||
title: AxisTitle(text: 'No of rooms'),
|
||||
),
|
||||
legend: Legend(isVisible: true),
|
||||
title: ChartTitle(text: 'Hotel Occupancy Rate'),
|
||||
enableSideBySideSeriesPlacement: true,
|
||||
tooltipBehavior: TooltipBehavior(enable: true),
|
||||
series: <CartesianSeries<ChartData, int>>[
|
||||
// First Series (value)
|
||||
|
||||
ColumnSeries<ChartData, int>(
|
||||
name: 'Available Rooms',
|
||||
// opacity: 0.9,
|
||||
// width: 0.4,
|
||||
color: Color(0xFF587BA3),
|
||||
dataSource: parseColumnChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => int.parse(data.timePeriod),
|
||||
yValueMapper: (ChartData data, _) => data.value1,
|
||||
),
|
||||
// Second Series (value1)
|
||||
ColumnSeries<ChartData, int>(
|
||||
color: Color(0xFF90B0D5),
|
||||
name: 'Occupied Rooms',
|
||||
dataSource: parseColumnChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => int.parse(data.timePeriod),
|
||||
yValueMapper: (ChartData data, _) => data.value,
|
||||
),
|
||||
], // Numeric Y-axis to plot the values
|
||||
);
|
||||
default:
|
||||
return Center(child: Text('Unknown chart type'));
|
||||
}
|
||||
@ -192,46 +304,84 @@ class ChartPageState extends State<ChartPage> {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF7DAFBC),
|
||||
appBar: AppBar(
|
||||
backgroundColor: Color(0xFF7DAFBC),
|
||||
backgroundColor: const Color(0xFF7DAFBC),
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios_new, color: Colors.white),
|
||||
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white),
|
||||
onPressed: () {
|
||||
context.go('/myhomepage');
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
title: const Text(
|
||||
'Charts',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
body: isLoading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: ListView.builder(
|
||||
itemCount: chartsData.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Card(
|
||||
margin: EdgeInsets.all(10),
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
children: [
|
||||
Card(
|
||||
margin: const EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Text(
|
||||
// 'Chart: ${chartsData[index]['chart_type']}',
|
||||
// style: TextStyle(
|
||||
// fontSize: 18, fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// SizedBox(height: 10),
|
||||
Container(
|
||||
height: 300,
|
||||
child: buildChart(chartsData[index]),
|
||||
children: const [
|
||||
Text(
|
||||
'Card 1 Title',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text('Content for the first card goes here.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Card(
|
||||
margin: const EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
Text(
|
||||
'Card 2 Title',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text('Content for the second card goes here.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: chartsData.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 300,
|
||||
child: buildChart(chartsData[index]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -240,23 +390,35 @@ class ChartPageState extends State<ChartPage> {
|
||||
class ChartData {
|
||||
final String timePeriod;
|
||||
final double value;
|
||||
final double value1;
|
||||
final String formattedValue;
|
||||
|
||||
ChartData({required this.timePeriod, required this.value})
|
||||
ChartData(
|
||||
{required this.timePeriod, required this.value, required this.value1})
|
||||
: formattedValue = formatNumber(value);
|
||||
}
|
||||
|
||||
// String formatNumber(double value) {
|
||||
// if (value >= 1e12) {
|
||||
// return '${(value / 1e12).toStringAsFixed(2)} T'; // Trillions
|
||||
// } else if (value >= 1e9) {
|
||||
// return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions
|
||||
// } else if (value >= 1e6) {
|
||||
// return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions
|
||||
// } else if (value >= 1e5) {
|
||||
// return '${(value / 1e5).toStringAsFixed(2)} L'; // Lakhs
|
||||
// } else if (value >= 1e3) {
|
||||
// return '${(value / 1e3).toStringAsFixed(2)} K'; // Thousands
|
||||
// }
|
||||
// return value.toStringAsFixed(2); // Default to two decimal places
|
||||
// }
|
||||
|
||||
String formatNumber(double value) {
|
||||
if (value >= 1e12) {
|
||||
return '${(value / 1e12).toStringAsFixed(2)} T'; // Trillions
|
||||
} else if (value >= 1e9) {
|
||||
if (value >= 1e9) {
|
||||
return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions
|
||||
} else if (value >= 1e6) {
|
||||
return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions
|
||||
} else if (value >= 1e5) {
|
||||
return '${(value / 1e5).toStringAsFixed(2)} L'; // Lakhs
|
||||
} else if (value >= 1e3) {
|
||||
return '${(value / 1e3).toStringAsFixed(2)} K'; // Thousands
|
||||
}
|
||||
return value.toStringAsFixed(2); // Default to two decimal places
|
||||
return value
|
||||
.toStringAsFixed(2); // Default to two decimal places for smaller numbers
|
||||
}
|
||||
|
||||
328
lib/presentation/Screens/demo_home2.dart
Normal file
328
lib/presentation/Screens/demo_home2.dart
Normal file
@ -0,0 +1,328 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: ChartPage2(dataSets: ''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChartPage2 extends StatefulWidget {
|
||||
final String dataSets;
|
||||
const ChartPage2({Key? key, required this.dataSets});
|
||||
@override
|
||||
ChartPageState createState() => ChartPageState();
|
||||
}
|
||||
|
||||
class ChartPageState extends State<ChartPage2> {
|
||||
List<dynamic> chartsData = [];
|
||||
bool isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchChartData(widget.dataSets);
|
||||
}
|
||||
|
||||
Future<void> fetchChartData(dataSets) async {
|
||||
const baseUrl =
|
||||
'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL
|
||||
final url = Uri.parse('$baseUrl?dataset=$dataSets');
|
||||
|
||||
try {
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
setState(() {
|
||||
chartsData = jsonDecode(response.body);
|
||||
isLoading = false;
|
||||
});
|
||||
} else {
|
||||
throw Exception('Failed to load data');
|
||||
}
|
||||
} catch (error) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
print('Error fetching data: $error');
|
||||
}
|
||||
}
|
||||
|
||||
List<ChartData> parseLineChartData(List<dynamic> response) {
|
||||
return response.map((entry) {
|
||||
// Attempt to fetch the TIMEPERIOD
|
||||
final rawTimePeriod =
|
||||
entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD'];
|
||||
final formattedTimePeriod = rawTimePeriod != null
|
||||
? rawTimePeriod.toString() // Use as-is if valid
|
||||
: 'Unknown'; // Fallback value if null
|
||||
|
||||
// Parse the value or fallback to 0.0
|
||||
final value =
|
||||
double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
|
||||
0.0;
|
||||
|
||||
return ChartData(
|
||||
timePeriod: formattedTimePeriod,
|
||||
value: value,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<ChartData> parseBarChartData(List<dynamic> response) {
|
||||
return response
|
||||
.asMap()
|
||||
.entries
|
||||
.map((entry) => ChartData(
|
||||
timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ??
|
||||
'Unknown', // Fallback to 'Unknown' if null
|
||||
value: double.tryParse(
|
||||
entry.value['ObsValue']['Value'].toString()) ??
|
||||
0.0, // Handle null/invalid value
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Update parseColumnChartData for column chart
|
||||
List<ChartData> parseColumnChartData(List<dynamic> response) {
|
||||
return response.map((entry) {
|
||||
// Extract TIME_PERIOD as x
|
||||
final x =
|
||||
int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0;
|
||||
// Extract Value as y
|
||||
final y =
|
||||
double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
|
||||
0.0;
|
||||
return ChartData(xInt: x, y: y);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Update parseScatterChartData for scatter chart
|
||||
List<ChartData> parseScatterChartData(List<dynamic> response) {
|
||||
return response.map((entry) {
|
||||
// Parse TIME_PERIOD into DateTime
|
||||
final timePeriod = entry['ObsKey']?['TIME_PERIOD'];
|
||||
final x = timePeriod != null
|
||||
? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime
|
||||
: DateTime.now(); // Fallback to current date if null
|
||||
|
||||
// Parse Value into double
|
||||
final y =
|
||||
double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ??
|
||||
0.0;
|
||||
|
||||
return ChartData(xDateTime: x, y: y);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Widget buildChart(dynamic chartData) {
|
||||
switch (chartData['chart_type']) {
|
||||
case 'scatter':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis
|
||||
tooltipBehavior: TooltipBehavior(enable: true),
|
||||
title: ChartTitle(text: 'Divorces'),
|
||||
series: <CartesianSeries>[
|
||||
ScatterSeries<ChartData, DateTime>(
|
||||
dataSource: parseScatterChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) =>
|
||||
data.xDateTime ?? DateTime.now(),
|
||||
yValueMapper: (ChartData data, _) => data.y,
|
||||
name: chartData['dataset'],
|
||||
dataLabelSettings: DataLabelSettings(isVisible: true),
|
||||
),
|
||||
],
|
||||
);
|
||||
case 'column':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: NumericAxis(
|
||||
title: AxisTitle(text: 'Year'),
|
||||
interval: 1, // Ensure no fractional intervals
|
||||
),
|
||||
title: ChartTitle(text: 'Marriages'),
|
||||
series: <CartesianSeries<ChartData, int>>[
|
||||
ColumnSeries<ChartData, int>(
|
||||
dataSource: parseColumnChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => data.xInt ?? 0,
|
||||
yValueMapper: (ChartData data, _) => data.y,
|
||||
name: chartData['dataset'],
|
||||
color: Color(0xFF7DAFBC),
|
||||
width: 0.8,
|
||||
spacing: 0.2,
|
||||
dataLabelSettings:
|
||||
DataLabelSettings(isVisible: true), // Show data labels
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
case 'line':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: CategoryAxis(),
|
||||
title: ChartTitle(text: 'Line Chart'),
|
||||
legend: Legend(isVisible: true),
|
||||
tooltipBehavior: TooltipBehavior(enable: true),
|
||||
series: <CartesianSeries<ChartData, String>>[
|
||||
LineSeries<ChartData, String>(
|
||||
dataSource: parseLineChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => data.timePeriod ?? '',
|
||||
yValueMapper: (ChartData data, _) => data.value ?? 0.0,
|
||||
name: 'Sales',
|
||||
dataLabelSettings: DataLabelSettings(isVisible: true),
|
||||
),
|
||||
],
|
||||
);
|
||||
case 'bar':
|
||||
return SfCartesianChart(
|
||||
primaryXAxis: CategoryAxis(),
|
||||
title: ChartTitle(text: 'Bar Chart'),
|
||||
tooltipBehavior: TooltipBehavior(enable: true),
|
||||
series: <CartesianSeries<ChartData, String>>[
|
||||
BarSeries<ChartData, String>(
|
||||
dataSource: parseBarChartData(chartData['response']),
|
||||
xValueMapper: (ChartData data, _) => data.timePeriod ?? '',
|
||||
yValueMapper: (ChartData data, _) => data.value ?? 0.0,
|
||||
name: 'Gold',
|
||||
color: Color.fromRGBO(8, 142, 255, 1),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
default:
|
||||
return Center(child: Text('Unknown chart type'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF7DAFBC),
|
||||
appBar: AppBar(
|
||||
backgroundColor: Color(0xFF7DAFBC),
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios_new, color: Colors.white),
|
||||
onPressed: () {
|
||||
context.go('/myhomepage');
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
'Charts',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
body: isLoading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
children: [
|
||||
// Row for two cards
|
||||
Row(
|
||||
children: [
|
||||
// First Card
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Card 1',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text('Content for Card 1 goes here.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Second Card
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Card 2',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text('Content for Card 2 goes here.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// List of charts
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: chartsData.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Card(
|
||||
margin: EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
height: 300,
|
||||
child: buildChart(chartsData[index]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChartData {
|
||||
final int? xInt; // For column and other charts, using int for x
|
||||
final DateTime? xDateTime; // For scatter charts, using DateTime for x
|
||||
final double y; // For column and other charts
|
||||
final String? timePeriod; // For line and bar charts
|
||||
final double? value; // For line and bar charts
|
||||
final double? y2; // For secondary data (stacked column chart)
|
||||
final double? y3; // For tertiary data (stacked column chart)
|
||||
final double? y4; // For quaternary data (stacked column chart)
|
||||
|
||||
// Constructor that handles all chart cases
|
||||
ChartData({
|
||||
this.xInt,
|
||||
this.xDateTime,
|
||||
this.y = 0.0,
|
||||
this.timePeriod,
|
||||
this.value,
|
||||
this.y2,
|
||||
this.y3,
|
||||
this.y4,
|
||||
});
|
||||
}
|
||||
18
lib/presentation/components/constant/constant.dart
Normal file
18
lib/presentation/components/constant/constant.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const Color economyColor = Color(0xFF90B0D5);
|
||||
const Color socialColor = Color(0xFFAA8E83);
|
||||
const Color environmentColor = Color(0xFF7DAFBC);
|
||||
const Color environmentboldColor = Color(0xFF90B0D5);
|
||||
const TextStyle subtitleStyle = TextStyle(
|
||||
fontFamily: 'Roboto', // Font family set to Roboto
|
||||
fontWeight: FontWeight.w400, // Regular weight (w400)
|
||||
fontSize: 11,
|
||||
color: Color(0xFF8E8E8E) // Font size 11
|
||||
);
|
||||
const TextStyle robotoRegular11 = TextStyle(
|
||||
fontFamily: 'Roboto', // Font family set to Roboto
|
||||
fontWeight: FontWeight.w400, // Regular weight (w400)
|
||||
fontSize: 11,
|
||||
color: Color(0xFF000000) // Font size 11
|
||||
);
|
||||
@ -171,6 +171,20 @@ class MyHomePage extends StatefulWidget {
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
void handleInfoCardClick(BuildContext context, String data) {
|
||||
// Handle navigation and pass dynamic data
|
||||
print(data);
|
||||
final dataSets = data;
|
||||
print(dataSets);
|
||||
if (dataSets == 'hotels') {
|
||||
context.go('/Chart/$dataSets');
|
||||
} else if (dataSets == 'divorces') {
|
||||
context.go('/Chart/$dataSets');
|
||||
} else if (dataSets == 'marriages') {
|
||||
context.go('/Chart/$dataSets');
|
||||
}
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -226,6 +240,7 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '1.62T',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () => handleInfoCardClick(context, 'Inflation'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Inflation Rate',
|
||||
@ -233,6 +248,7 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '4.82%',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () => handleInfoCardClick(context, 'Inflation'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -247,6 +263,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '215.4B',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'commodities'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Hotel Guests',
|
||||
@ -254,6 +272,7 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '25.21M',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () => handleInfoCardClick(context, 'hotels'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -280,18 +299,22 @@ class EconomyStats extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InfoCard(
|
||||
title: 'Population',
|
||||
subtitle: '(2022) (AED)',
|
||||
value: '1.62T',
|
||||
title: 'Divorces',
|
||||
subtitle: '(2022)',
|
||||
value: '0',
|
||||
bordercolor: Colors.brown[200]!,
|
||||
textcolor: Colors.brown[200]!,
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'divorces'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Labor Force',
|
||||
title: 'Marriages',
|
||||
subtitle: '(2022)',
|
||||
value: '4.82%',
|
||||
value: '0',
|
||||
bordercolor: Colors.brown[200]!,
|
||||
textcolor: Colors.brown[200]!,
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'marriages'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -301,11 +324,13 @@ class EconomyStats extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InfoCard(
|
||||
title: 'Hospitals (Gov )',
|
||||
title: 'Hospitals (Gov)',
|
||||
subtitle: '(Jan - Jan 2024) - AED',
|
||||
value: '215.4B',
|
||||
bordercolor: Colors.brown[200]!,
|
||||
textcolor: Colors.brown[200]!,
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'Hospitals'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Schools',
|
||||
@ -313,6 +338,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '25.21M',
|
||||
bordercolor: Colors.brown[200]!,
|
||||
textcolor: Colors.brown[200]!,
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'Schools'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -345,6 +372,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '1.62T',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () => handleInfoCardClick(
|
||||
context, 'Electricity'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Cruid oil',
|
||||
@ -352,6 +381,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '4.82%',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'Cruid'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -367,6 +398,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '215.4B',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () =>
|
||||
handleInfoCardClick(context, 'Export'),
|
||||
),
|
||||
InfoCard(
|
||||
title: 'Desalinated water',
|
||||
@ -374,6 +407,8 @@ class EconomyStats extends StatelessWidget {
|
||||
value: '25.21M',
|
||||
bordercolor: Color(0xFF7DAFBC),
|
||||
textcolor: Color(0xFF7DAFBC),
|
||||
onTap: () => handleInfoCardClick(
|
||||
context, 'Desalinated'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -433,6 +468,7 @@ class InfoCard extends StatelessWidget {
|
||||
final String value;
|
||||
final Color bordercolor;
|
||||
final Color? textcolor;
|
||||
final VoidCallback onTap; // New callback parameter
|
||||
|
||||
const InfoCard({
|
||||
Key? key,
|
||||
@ -441,6 +477,7 @@ class InfoCard extends StatelessWidget {
|
||||
required this.value,
|
||||
required this.bordercolor,
|
||||
this.textcolor,
|
||||
required this.onTap, // Initialize in constructor
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -450,9 +487,7 @@ class InfoCard extends StatelessWidget {
|
||||
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.go('/DemoHome');
|
||||
},
|
||||
onTap: onTap, // Use callback for handling clicks
|
||||
child: Container(
|
||||
height: myheight / 9,
|
||||
width: mywidth / 4,
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class BaseScaffold extends StatelessWidget {
|
||||
class BaseScaffold extends StatefulWidget {
|
||||
final Widget body;
|
||||
final Widget title;
|
||||
final List<Widget>? actions;
|
||||
@ -17,6 +20,81 @@ class BaseScaffold extends StatelessWidget {
|
||||
this.onBackIconPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_BaseScaffoldState createState() => _BaseScaffoldState();
|
||||
}
|
||||
|
||||
class _BaseScaffoldState extends State<BaseScaffold> {
|
||||
final _pb = PocketBase('https://pb.venbait.in');
|
||||
String _avatarUrl = '';
|
||||
dynamic userId;
|
||||
String? userName;
|
||||
String? userEmail;
|
||||
String? userAvatar;
|
||||
String? role;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkUserId();
|
||||
}
|
||||
|
||||
// Method to retrieve userId from SharedPreferences
|
||||
Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('userId'); // Retrieve the userId
|
||||
}
|
||||
|
||||
// Method to check if userId exists and update state
|
||||
Future<void> _checkUserId() async {
|
||||
String? fetchedUserId = await getUserId();
|
||||
if (fetchedUserId != null && fetchedUserId.isNotEmpty) {
|
||||
setState(() {
|
||||
userId = fetchedUserId;
|
||||
});
|
||||
//print('NAVUser ID: $userId');
|
||||
_fetchUserData();
|
||||
} else {
|
||||
print('No userId found');
|
||||
// Handle the case where userId is not available
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchUserData() async {
|
||||
try {
|
||||
final adminAuth = await _pb.admins
|
||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||
final adminToken = adminAuth.token;
|
||||
//print('adminToken- ${adminToken}');
|
||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||
userId!,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $adminToken',
|
||||
},
|
||||
);
|
||||
print('NAVuserDetails: $userDetailsResponse');
|
||||
|
||||
setState(() {
|
||||
userName = userDetailsResponse.data['username'] ?? '';
|
||||
userEmail = userDetailsResponse.data['email'] ?? '';
|
||||
userAvatar = userDetailsResponse.data['avatar'] ?? '';
|
||||
role = userDetailsResponse.data['role'] ?? '';
|
||||
String recordId = userId;
|
||||
String collectionId =
|
||||
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||
|
||||
if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
|
||||
_avatarUrl =
|
||||
'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar';
|
||||
} else {
|
||||
_avatarUrl = ''; // Reset to default or empty
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error fetching user details: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -24,30 +102,34 @@ class BaseScaffold extends StatelessWidget {
|
||||
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,
|
||||
leading:showBackIcon
|
||||
? IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.arrow_back_ios_new_sharp),
|
||||
onPressed: onBackIconPressed ??
|
||||
() {
|
||||
Navigator.of(context).pop(); // Default back button behavior
|
||||
},
|
||||
) : null,
|
||||
actions: [
|
||||
IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)),
|
||||
if (showBackIcon)
|
||||
Builder(
|
||||
builder: (context) => IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer(); // Opens the drawer
|
||||
},
|
||||
),
|
||||
),
|
||||
],),
|
||||
appBar: AppBar(
|
||||
title: widget.title,
|
||||
leading:widget.showBackIcon
|
||||
? IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.arrow_back_ios_new_sharp),
|
||||
onPressed: widget.onBackIconPressed ??
|
||||
() {
|
||||
Navigator.of(context).pop(); // Default back button behavior
|
||||
},
|
||||
) : null,
|
||||
actions: [
|
||||
// IconButton(
|
||||
// onPressed: () {
|
||||
// // Share logic here
|
||||
// print("Share icon pressed");
|
||||
// Share.share(
|
||||
// 'Open the app: fcscapp://home\n\n'
|
||||
// 'If you don’t have the app installed, '
|
||||
// 'visit: http://localhost:65493');
|
||||
// },
|
||||
// icon: Icon(Icons.share),
|
||||
// ),
|
||||
IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)),
|
||||
],
|
||||
),
|
||||
drawer: Drawer(
|
||||
child: ListView(
|
||||
children: [
|
||||
@ -70,18 +152,39 @@ class BaseScaffold extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: mywidth / 8,
|
||||
child: Image(
|
||||
image: AssetImage(
|
||||
'assets/edit_profile/profile.png'))),
|
||||
width: mywidth / 8,
|
||||
height: mywidth / 8,
|
||||
child: ClipOval(
|
||||
child: _avatarUrl.isNotEmpty
|
||||
? Image(
|
||||
image: NetworkImage(_avatarUrl),
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
)
|
||||
: Image(
|
||||
image: AssetImage(
|
||||
'assets/edit_profile/profile.png'),
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
),
|
||||
),
|
||||
//child: Image(image: AssetImage('assets/edit_profile/profile.png'))
|
||||
),
|
||||
SizedBox(
|
||||
width: mywidth / 20,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Mohammad'),
|
||||
Text('Mohammad@fcsc.com')
|
||||
// Text(userId ?? 'Loading user...'), // Display userId here
|
||||
// Text('Mohammad@fcsc.com')
|
||||
Text(userName ?? 'Loading...'),
|
||||
Text(
|
||||
userEmail ?? 'Loading...',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
@ -90,34 +193,45 @@ class BaseScaffold extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (role != 'admin')
|
||||
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'),
|
||||
),
|
||||
if (role == 'admin')
|
||||
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/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'))),
|
||||
child:
|
||||
Image(image: AssetImage('assets/icons/drawer/book.png'))),
|
||||
title: Text('User Guide'),
|
||||
onTap: () => context.go('/user-guide'),
|
||||
),
|
||||
ListTile(
|
||||
leading: SizedBox(
|
||||
height: myheight / 15,
|
||||
width: mywidth / 15,
|
||||
child: Image(
|
||||
image: AssetImage('assets/icons/drawer/logout.png'))),
|
||||
title: Text('Logout'),
|
||||
onTap: () => context.go('/'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -162,14 +276,14 @@ class BaseScaffold extends StatelessWidget {
|
||||
unselectedItemColor: Colors.grey,
|
||||
showUnselectedLabels: true,
|
||||
),
|
||||
body: body,
|
||||
body: widget.body,
|
||||
);
|
||||
}
|
||||
|
||||
//Map the current route to the selected index
|
||||
// Map the current route to the selected index
|
||||
int _getSelectedIndex(String route) {
|
||||
switch (route) {
|
||||
case '/':
|
||||
case '/myhomepage':
|
||||
return 0;
|
||||
case '/about':
|
||||
return 1;
|
||||
@ -186,7 +300,7 @@ class BaseScaffold extends StatelessWidget {
|
||||
void _onItemTapped(BuildContext context, int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
context.go('/');
|
||||
context.go('/myhomepage');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/about');
|
||||
|
||||
@ -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.69.2
|
||||
marquee: ^2.2.3
|
||||
url_launcher: ^6.3.1
|
||||
# dynamic_layouts:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user