bookmark added

This commit is contained in:
venbaittech 2025-02-10 18:22:40 +05:30
parent e0fd4267f2
commit 3dd2aa40fe
7 changed files with 788 additions and 232 deletions

View File

@ -15,12 +15,12 @@ if (localPropertiesFile.exists()) {
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "11"
flutterVersionCode = "12"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0.10"
flutterVersionName = "1.0.11"
}
def keystorePropertiesFile = rootProject.file("key.properties")

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

View File

@ -0,0 +1,29 @@
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter/material.dart';
class ToastUtil {
/// **General Toast**
static void showToast(String message,
{Color bgColor = Colors.black, Color textColor = Colors.white}) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: bgColor,
textColor: textColor,
fontSize: 16.0,
);
}
/// **Success Toast**
static void showSuccessToast(String message) {
showToast(message,
bgColor: Color(0xFFD6E9C6), textColor: Color(0xFF2F692C));
}
/// **Delete Toast**
static void showDeleteToast(String message) {
showToast(message,
bgColor: Color(0xFFEB5F24), textColor: Color(0xFF544C4C));
}
}

View File

@ -3,5 +3,6 @@ import 'package:flutter/material.dart';
abstract class UaeNumbersAssetPath {
static const _basePath = 'assets/icons/uae_numbers';
static const bookmarksUae = '$_basePath/bookmarks.png';
static const bookmarksSelectedUae = '$_basePath/bookmarksSelectedUae.png';
static const shareUae = '$_basePath/share.png';
}
}

View File

@ -2,7 +2,10 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
import 'package:uae_stat/config/toast_util.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/uae_numbers_asset_path.dart';
import 'package:uae_stat/presentation/Screens/charts/services/api_service.dart';
@ -34,6 +37,7 @@ class ChartScreen1 extends ConsumerStatefulWidget {
}
class _ChartScreen1State extends ConsumerState<ChartScreen1> {
final _pb = PocketBase('https://pb.venbait.in');
List<Map<String, dynamic>> selectedFiltersStorage = [];
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final ApiService apiService = ApiService();
@ -60,7 +64,9 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
late TutorialCoachMark tutorialCoachMark;
final GlobalKey chartKey = GlobalKey();
final GlobalKey bookMarkKey = GlobalKey();
final GlobalKey cardKey= GlobalKey();
final GlobalKey cardKey = GlobalKey();
bool isBookmarked = false; // Track bookmark state
String? bookmarkId; // Stores the ID of the bookmark record in PocketBase
@override
void initState() {
@ -68,6 +74,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
final String bgColor = widget.bgColor;
print(' bgColor $bgColor');
// fetchChartData(widget.dataSets);
checkIfBookmarked();
final locale = ref.read(localeProvider);
fetchChartData(widget.dataSets, locale?.languageCode ?? 'en').then((_) {
if (_tabsData.isNotEmpty) {
@ -81,13 +88,317 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
});
}
/// Check if the item is already bookmarked
Future<void> checkIfBookmarked() async {
final prefs = await SharedPreferences.getInstance();
final userID = prefs.getString('userId');
try {
final adminAuth = await _pb.admins.authWithPassword(
'pb@venbainfotech.com',
'pb@venbainfotech.com',
);
final adminToken = adminAuth.token;
final result = await _pb.collection('bookmark').getList(
filter: "user_id = '${userID}' && dataset = '${widget.dataSets}'",
headers: {
'Authorization': adminToken, // Pass the admin token
},
);
if (result.items.isNotEmpty) {
setState(() {
isBookmarked = true;
bookmarkId = result.items.first.id;
});
}
} catch (e) {
print("Error checking bookmark: $e");
}
}
/// Add bookmark to PocketBase
Future<void> addBookmark() async {
final prefs = await SharedPreferences.getInstance();
final userID = prefs.getString('userId');
try {
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final adminToken = adminAuth.token;
final response = await _pb.collection('bookmark').create(body: {
'user_id': userID,
'dataset': widget.dataSets,
'bgColor': widget.bgColor,
'mainTopic': widget.mainTopic,
'title': widget.title,
'keyParam': widget.keyParam,
}, headers: {
'Authorization': adminToken
});
setState(() {
isBookmarked = true;
bookmarkId = response.id;
});
// ToastUtil.showSuccessToast("Added to Bookmark.");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Added to Bookmark.',
style: TextStyle(
color: Color(0xFF2F692C), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
),
);
print("Bookmark added: ${response.id}");
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to Add',
style: TextStyle(
color: Color(0xFF544C4C), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFEB5F24),
duration: Duration(seconds: 2),
),
);
// ToastUtil.showDeleteToast("Unable to add to bookmarks. Please try again");
print("Error adding bookmark: $e");
}
}
/// Remove bookmark from PocketBase
Future<void> removeBookmark() async {
try {
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final adminToken = adminAuth.token;
if (bookmarkId != null) {
await _pb.collection('bookmark').delete(bookmarkId!, headers: {
'Authorization': adminToken,
});
setState(() {
isBookmarked = false;
bookmarkId = null;
});
// ToastUtil.showSuccessToast("Removed from Bookmark.");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Removed from Bookmark.',
style: TextStyle(
color: Color(0xFF2F692C), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
),
);
print("Bookmark removed");
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to Remove',
style: TextStyle(
color: Color(0xFF544C4C), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFEB5F24),
duration: Duration(seconds: 2),
),
);
// ToastUtil.showDeleteToast(
// "Unable to remove from bookmarks. Please try again");
print("Error removing bookmark: $e");
}
}
/// Show confirmation dialog before removing bookmark
void showRemoveBookmarkDialog() {
double myheight = MediaQuery.of(context).size.height;
showDialog(
context: context,
barrierDismissible: false, // User must tap button to dismiss dialog
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), // Rounded corners
),
contentPadding: EdgeInsets.zero,
content: Stack(
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: myheight / 30,
),
Text(
context.translate(
'Are you sure you want to remove this bookmark?',
'هل أنت متأكد أنك تريد إزالة هذه الإشارة المرجعية؟'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Color(0xFF898C81),
),
)
],
),
),
],
),
actions: [
SizedBox(height: 20),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10.0), // Adjust the radius as needed
),
side: BorderSide(
color: Color(0xFFAA8E83), // Set the outline color
width: 1, // Set the border width
),
),
child: Text(
context.translate('No', 'لا'),
style: TextStyle(
color: Color(0xFFAA8E83),
fontSize: 16,
),
),
),
),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () {
Navigator.pop(context);
removeBookmark(); // Close dialog
},
style: TextButton.styleFrom(
backgroundColor: Color(0xFFAA8E83), // Set background color
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Set text color
),
),
child: Text(
context.translate('Yes', 'نعم'),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
],
),
);
}
void showAddBookmarkDialog() {
double myheight = MediaQuery.of(context).size.height;
showDialog(
context: context,
barrierDismissible: false, // User must tap button to dismiss dialog
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), // Rounded corners
),
contentPadding: EdgeInsets.zero,
content: Stack(
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: myheight / 30,
),
Text(
context.translate(
'Do you want to add this to bookmarks?',
'هل تريد إضافة هذا إلى الإشارات المرجعية؟',
),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Color(0xFF898C81),
),
)
],
),
),
],
),
actions: [
SizedBox(height: 20),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10.0), // Adjust the radius as needed
),
side: BorderSide(
color: Color(0xFFAA8E83), // Set the outline color
width: 1, // Set the border width
),
),
child: Text(
context.translate('No', 'لا'),
style: TextStyle(
color: Color(0xFFAA8E83),
fontSize: 16,
),
),
),
),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () {
Navigator.pop(context);
addBookmark(); // Close dialog
},
style: TextButton.styleFrom(
backgroundColor: Color(0xFFAA8E83), // Set background color
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Set text color
),
),
child: Text(
context.translate('Yes', 'نعم'),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
],
),
);
}
void handleSkip() {
tutorialCoachMark.skip();
debugPrint('Skip clicked');
ref.read(chartsTourProvider.notifier).state = true;
ref.read(previousChartsTourProvider.notifier).state = true;
ref.read(homeTourProvider.notifier).state = true;
ref.read( previousHomeTourProvider.notifier).state = true;
ref.read(chartsTourProvider.notifier).state = true;
ref.read(previousChartsTourProvider.notifier).state = true;
ref.read(homeTourProvider.notifier).state = true;
ref.read(previousHomeTourProvider.notifier).state = true;
ref.read(scaffoldTourProvider.notifier).state = true;
ref.read(previousScaffoldTourProvider.notifier).state = true;
context.go('/myhomepage');
@ -101,7 +412,6 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
tutorialCoachMark = TutorialCoachMark(
paddingFocus: 0,
useSafeArea: true,
pulseEnable: false,
hideSkip: true,
targets: marriageTargets,
@ -109,26 +419,22 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
context.go('/myhomepage');
debugPrint('Marriage Tutorial Finished');
},
)..show(context: context);
}
void _startTutorialAfterRender() {
final chartTour = ref.watch(chartsTourProvider);
final previousChartTour = ref.watch(previousChartsTourProvider);
debugPrint('render problem');
if (cardKey.currentContext != null && !chartTour) {
_kpiTutorialCoachMark();
} else if(cardKey.currentContext != null && !previousChartTour) {
} else if (cardKey.currentContext != null && !previousChartTour) {
_previousKpiTutorial();
}
else if (!chartTour || !previousChartTour){
} else if (!chartTour || !previousChartTour) {
Future.delayed(Duration(milliseconds: 100), _startTutorialAfterRender);
}else{
} else {
return;
}
}
void _previousKpiTutorial() {
@ -143,14 +449,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
context.go('/myhomepage');
debugPrint('Previous Marriage Tutorial Finished');
},
)..show(context: context);
}
void _initmarriageTargets(){
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
marriageTargets=[
void _initmarriageTargets() {
final double screenWidth = MediaQuery.of(context).size.width;
final double screenHeight = MediaQuery.of(context).size.height;
marriageTargets = [
TargetFocus(
identify: 'cardKey',
keyTarget: cardKey,
@ -161,14 +466,14 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
createTargetContent(
text: AppLocalizations.of(context)!.kpiCards,
alignment: ContentAlign.top,
gap:55,
gap: 55,
space: 0,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.3,
height: MediaQuery.of(context).size.height * 0.3,
child: Stack(
children: [
Positioned(
@ -179,15 +484,15 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
children: [
Align(
alignment: Alignment.bottomRight,
child:Padding(
padding: EdgeInsets.only(right:10),
child:Text(
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
'3/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
) ,
),
),
),
const SizedBox(height: 5),
@ -217,14 +522,21 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(previousHomeTourProvider.notifier).state=false;
ref
.read(chartsTourProvider.notifier)
.state = true;
ref
.read(
previousHomeTourProvider.notifier)
.state = false;
tutorialCoachMark.finish();
},
),
@ -234,11 +546,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color:Color(0xFF7DAFBC), width: 1.5),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
@ -256,25 +570,25 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
),
TargetContent(
padding: EdgeInsets.only(bottom: 0,top : 0,),
padding: EdgeInsets.only(
bottom: 0,
top: 0,
),
align: ContentAlign.top,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height*0.69 ,
child: Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width*0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]
),
height: MediaQuery.of(context).size.height * 0.69,
child: Stack(children: [
Positioned(
left: MediaQuery.of(context).size.width * 0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]),
),
),
],
@ -397,7 +711,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.60,
height: MediaQuery.of(context).size.height * 0.60,
child: Stack(
children: [
Positioned(
@ -408,8 +722,9 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(padding: EdgeInsets.only(right: 10),
child: Text(
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
'4/7',
style: const TextStyle(
color: Colors.white,
@ -445,11 +760,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
@ -460,14 +777,20 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color: Color(0xFF7DAFBC), width: 1),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(scaffoldTourProvider.notifier).state=false;
ref
.read(chartsTourProvider.notifier)
.state = true;
ref
.read(scaffoldTourProvider.notifier)
.state = false;
tutorialCoachMark.finish();
},
),
@ -484,10 +807,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
),
TargetContent(
padding: EdgeInsets.only(left:0, top:0),
padding: EdgeInsets.only(left: 0, top: 0),
align: ContentAlign.right,
child: Container(
width: screenWidth/4,
width: screenWidth / 4,
height: screenHeight,
child: Stack(
children: [
@ -513,14 +836,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
],
),
];
}
void _initpreviousTargets(){
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
previousMarriageTargets=[
void _initpreviousTargets() {
final double screenWidth = MediaQuery.of(context).size.width;
final double screenHeight = MediaQuery.of(context).size.height;
previousMarriageTargets = [
TargetFocus(
identify: 'BookMarkKey',
keyTarget: bookMarkKey,
@ -538,7 +860,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.60,
height: MediaQuery.of(context).size.height * 0.60,
child: Stack(
children: [
Positioned(
@ -549,7 +871,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
children: [
Align(
alignment: Alignment.bottomRight,
child:Padding(
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
'4/7',
@ -587,11 +909,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
@ -602,14 +926,20 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color:Color(0xFF7DAFBC), width: 1.5),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(scaffoldTourProvider.notifier).state=false;
ref
.read(chartsTourProvider.notifier)
.state = true;
ref
.read(scaffoldTourProvider.notifier)
.state = false;
tutorialCoachMark.finish();
},
),
@ -626,10 +956,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
),
TargetContent(
padding: EdgeInsets.only(left:0, top: 10),
padding: EdgeInsets.only(left: 0, top: 10),
align: ContentAlign.right,
child: Container(
width: screenWidth/4,
width: screenWidth / 4,
height: screenHeight,
child: Stack(
children: [
@ -666,13 +996,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
text: AppLocalizations.of(context)!.kpiCards,
space: 0,
alignment: ContentAlign.top,
gap:50,
gap: 50,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.30,
height: MediaQuery.of(context).size.height * 0.30,
child: Stack(
children: [
Positioned(
@ -681,9 +1011,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
right: 16,
child: Column(
children: [
Align(alignment: Alignment.bottomRight,
child:Padding(
padding: const EdgeInsets.only(right:10.0),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
'3/7',
style: const TextStyle(
@ -691,7 +1022,8 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
fontSize: 14,
),
),
),),
),
),
const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -719,14 +1051,22 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
ref.read(previousChartsTourProvider.notifier).state=true;
ref.read(previousHomeTourProvider.notifier).state=false;
ref
.read(previousChartsTourProvider
.notifier)
.state = true;
ref
.read(
previousHomeTourProvider.notifier)
.state = false;
tutorialCoachMark.finish();
},
),
@ -736,11 +1076,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color:Color(0xFF7DAFBC), width: 1.5),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
@ -758,31 +1100,29 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
),
TargetContent(
padding: EdgeInsets.only(bottom: 0,top : 0,),
padding: EdgeInsets.only(
bottom: 0,
top: 0,
),
align: ContentAlign.top,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height*0.69 ,
child: Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width*0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]
),
height: MediaQuery.of(context).size.height * 0.69,
child: Stack(children: [
Positioned(
left: MediaQuery.of(context).size.width * 0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]),
),
),
],
),
];
}
@ -1295,8 +1635,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
});
selectedFiltersStorage.clear();
Navigator.pop(context);
},
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF92722A),
shape: RoundedRectangleBorder(
@ -1305,7 +1644,8 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 0.0, vertical: 14.2), // Added padding
horizontal: 0.0,
vertical: 14.2), // Added padding
child: Text(
context.translate('Clear', 'واضح'),
style: TextStyle(
@ -1343,7 +1683,8 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 0.0, vertical: 16.0), // Added padding
horizontal: 0.0,
vertical: 16.0), // Added padding
child: FittedBox(
fit: BoxFit
.scaleDown, // Ensures text resizes if necessary
@ -1360,7 +1701,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
maxLines: 1, // Ensures single line
),
),
),
),
),
),
],
@ -1602,11 +1943,35 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
),
),
SizedBox(width: 5),
Image.asset(
UaeNumbersAssetPath.bookmarksUae,
color: Colors.white,
width: 24,
height: 24,
GestureDetector(
onTap: () {
if (isBookmarked) {
showRemoveBookmarkDialog();
} else {
showAddBookmarkDialog();
}
},
child: Row(
children: [
Text(
"Bookmark",
style: const TextStyle(
fontSize: 16,
color: Colors.white),
),
const SizedBox(width: 5),
Image.asset(
isBookmarked
? UaeNumbersAssetPath
.bookmarksSelectedUae // Filled bookmark image
: UaeNumbersAssetPath
.bookmarksUae, // Default bookmark image
// color: Colors.white,
width: 24,
height: 24,
),
],
),
),
],
),
@ -1695,7 +2060,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Padding(
padding: const EdgeInsets.all(3.34),
child: GridView.builder(
key:cardKey,
key: cardKey,
itemCount: cardData.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),

View File

@ -1,6 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:dio/dio.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
class BookMark extends ConsumerStatefulWidget {
@ -11,93 +16,284 @@ class BookMark extends ConsumerStatefulWidget {
}
class _BookMarkState extends ConsumerState<BookMark> {
// Example dynamic data
final List<Map<String, dynamic>> dataList = [
{
'title': 'GDP(Constant)',
'subtitle': '(2022)(AED)',
'value': '1.62T',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Trade Value',
'subtitle': '(Jan-Jan 2024)-AED',
'value': '215.4B',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Electricity Production',
'subtitle': '(2022) (GWh)',
'value': '155,438',
'valueColor': Color(0xFFAA8E83),
'isBookmark': true
},
{
'title': 'Crude Oil Production',
'subtitle': '(2022) (1000 b/d)',
'value': '3,064',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Quantitiy of Export Oil',
'subtitle': '(2022) (1000 b/d)',
'value': '2717',
'valueColor': Color(0xFF7DAFBC),
'isBookmark': true
},
{
'title': 'Desalinated Water Production',
'subtitle': '(2022) (MCM)',
'value': '1823.8',
'valueColor': Color(0xFF7DAFBC),
'isBookmark': true
},
];
final _pb = PocketBase('https://pb.venbait.in');
List<Map<String, dynamic>> dataList = [];
bool isLoading = true;
@override
Widget build(BuildContext context) {
final filteredList =
dataList.where((item) => item['isBookmark'] == true).toList();
return PopScope(
canPop: false, // Allow back navigation only if not login screen
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/myhomepage'); // Show exit confirmation dialog
},
child: BaseScaffold(
title: Text('Bookmark'),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Two columns
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
mainAxisExtent: 100,
),
itemCount: filteredList.length,
itemBuilder: (context, index) {
return _buildBox(filteredList[index], context);
},
void initState() {
super.initState();
final locale = ref.read(localeProvider);
fetchBookmarks(locale?.languageCode ?? 'en');
}
// Function to convert hex color string to int
Color _parseColor(String? colorString) {
if (colorString == null || colorString.isEmpty) {
return const Color(0xFF80A8CD); // Default color
}
try {
return Color(int.parse(colorString.replaceFirst('#', '0xFF')));
} catch (e) {
debugPrint("Invalid color format: $colorString");
return const Color(0xFF80A8CD); // Fallback color
}
}
Future<void> fetchBookmarks(locale) async {
try {
final prefs = await SharedPreferences.getInstance();
final userID = prefs.getString('userId') ?? '';
if (userID.isEmpty) {
setState(() {
isLoading = false;
});
return;
}
final response = await Dio().get(
'https://pb.venbait.in/api/getUserBookmark',
queryParameters: {'user_id': userID, 'language': locale},
);
if (response.statusCode == 200 && response.data != null) {
if (response.data is Map<String, dynamic> &&
response.data.containsKey("error")) {
// API returned an error message instead of bookmark data
setState(() {
dataList = []; // Clear the list
});
print("No bookmarks found.");
} else {
List<dynamic> fetchedData = response.data;
setState(() {
dataList = fetchedData.map((item) {
return {
'main_topic': item['main_topic'] ?? '',
'title': item['data_set_tile_heading'] ?? '',
'subtitle': item['sub_topic'] ?? '',
'value': item['value'] ?? '',
'value_source': item['value_source'] ?? '',
'data_set': item['data_set'] ?? '',
'valueColor': _parseColor(item['color_pattern']),
'isBookmark': true,
'id': item['id'],
};
}).toList();
print('dataList $dataList');
});
}
}
} catch (e) {
setState(() {
dataList = []; // Clear the list
});
debugPrint("Error fetching bookmarks: $e");
} finally {
setState(() {
isLoading = false;
});
}
}
/// Remove bookmark from PocketBase
Future<void> removeBookmark(bookmarkId) async {
try {
final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final adminToken = adminAuth.token;
await _pb.collection('bookmark').delete(bookmarkId!, headers: {
'Authorization': adminToken,
});
// Show success SnackBar
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Removed from Bookmark.',
style: TextStyle(
color: Color(0xFF2F692C), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
),
);
final locale = ref.read(localeProvider);
fetchBookmarks(locale?.languageCode ?? 'en');
// ToastUtil.showSuccessToast("Removed from Bookmark.");
print("Bookmark removed");
} catch (e) {
SnackBar(
content: Text(
'Unable to remove from bookmarks. Please try again',
style:
TextStyle(color: Color(0xFFEB5F24), fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
);
print("Error removing bookmark: $e");
}
}
/// Show confirmation dialog before removing bookmark
void showRemoveBookmarkDialog(id) {
double myheight = MediaQuery.of(context).size.height;
showDialog(
context: context,
barrierDismissible: false, // User must tap button to dismiss dialog
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0), // Rounded corners
),
contentPadding: EdgeInsets.zero,
content: Stack(
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: myheight / 30,
),
Text(
context.translate(
'Are you sure you want to remove this bookmark?',
'هل أنت متأكد أنك تريد إزالة هذه الإشارة المرجعية؟'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Color(0xFF898C81),
),
)
],
),
),
],
),
actions: [
SizedBox(height: 20),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10.0), // Adjust the radius as needed
),
side: BorderSide(
color: Color(0xFFAA8E83), // Set the outline color
width: 1, // Set the border width
),
),
child: Text(
context.translate('No', 'لا'),
style: TextStyle(
color: Color(0xFFAA8E83),
fontSize: 16,
),
),
),
),
SizedBox(
width: 100, // Set the desired width
child: TextButton(
onPressed: () {
Navigator.pop(context);
removeBookmark(id); // Close dialog
},
style: TextButton.styleFrom(
backgroundColor: Color(0xFFAA8E83), // Set background color
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Set text color
),
),
child: Text(
context.translate('Yes', 'نعم'),
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
],
),
);
}
String colorToHex(Color color) {
return '0xFF${color.red.toRadixString(16).padLeft(2, '0').toUpperCase()}'
'${color.green.toRadixString(16).padLeft(2, '0').toUpperCase()}'
'${color.blue.toRadixString(16).padLeft(2, '0').toUpperCase()}';
}
@override
Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchBookmarks(localeCode);
});
final filteredList =
dataList.where((item) => item['isBookmark'] == true).toList();
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/myhomepage');
},
child: BaseScaffold(
title: Text(context.translate('Bookmark', 'إشارة مرجعية')),
body: isLoading
? const Center(child: CircularProgressIndicator())
: filteredList.isEmpty
? Center(
child: Text(context.translate('No bookmarks found',
'لم يتم العثور على إشارات مرجعية')))
: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
mainAxisExtent: 100,
),
itemCount: filteredList.length,
itemBuilder: (context, index) {
return _buildBox(filteredList[index], context);
},
),
),
),
);
}
// Widget for creating a box
Widget _buildBox(Map<String, dynamic> data, BuildContext context) {
// Color borderColor = data['valueColor'];
return GestureDetector(
onTap: () {
final data_set = data['data_set'];
final Color colorPattern = data['valueColor'];
final encodedMainTopic = data['main_topic'];
final encodedTitle = data['title'];
String hexColor = colorToHex(colorPattern); // Get hex string
print(hexColor); // Prints: 0xFFAA8E83
debugPrint("Box clicked: ${data['title']}");
context.go(
'/chartScreen/$data_set?bgColor=$hexColor&mainTopic=$encodedMainTopic&title=$encodedTitle');
},
child: Container(
width: MediaQuery.of(context).size.width * 0.4,
height: 100,
padding: EdgeInsets.all(8.0),
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
@ -120,54 +316,18 @@ class _BookMarkState extends ConsumerState<BookMark> {
alignment: Alignment.center,
child: Text(
data['title'] ?? '',
style: TextStyle(
fontWeight: FontWeight.w500,
),
style: const TextStyle(fontWeight: FontWeight.w500),
overflow: TextOverflow.ellipsis,
// Truncate long text
maxLines: 1, // Limit to 1 line
maxLines: 1,
),
),
),
GestureDetector(
onTap: () {
debugPrint("Icon clicked in: ${data['title']}");
setState(() {
// Change isOpen to false when icon is clicked
int index = dataList.indexWhere(
(item) => item['title'] == data['title'],
);
if (index != -1) {
dataList[index]['isBookmark'] = false;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Removed From Bookmark',
style: TextStyle(
color: Color(0xFF2F692C),
fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to Remove',
style: TextStyle(
color: Color(0xFFEB5F24),
fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFF544C4C),
duration: Duration(seconds: 2),
),
);
}
});
showRemoveBookmarkDialog(data['id']);
},
child: Icon(
child: const Icon(
Icons.bookmarks_rounded,
color: Colors.black,
size: 20,
@ -176,8 +336,8 @@ class _BookMarkState extends ConsumerState<BookMark> {
],
),
Text(
data['subtitle'] ?? '',
style: TextStyle(
data['value_source'] ?? '',
style: const TextStyle(
fontWeight: FontWeight.w500, color: Colors.grey),
overflow: TextOverflow.ellipsis,
maxLines: 1,

View File

@ -1,7 +1,7 @@
name: uae_stat
description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness."
publish_to: "none"
version: 1.0.10+11
version: 1.0.11+12
environment:
sdk: ">=3.2.3 <4.0.0"
@ -37,7 +37,7 @@ dependencies:
pocketbase: ^0.18.1
flutter_secure_storage: ^9.0.0
jwt_decoder: ^2.0.1
fluttertoast: ^8.2.5
fluttertoast: ^8.2.11
webview_flutter: ^4.5.0
video_player: ^2.8.5
flutter_carousel_widget: ^3.1.0
@ -113,6 +113,7 @@ flutter:
- assets/edit_profile/
- assets/splash_screen/
- assets/icons/uae_numbers/bookmarks.png
- assets/icons/uae_numbers/bookmarksSelectedUae.png
- assets/icons/uae_numbers/share.png
- assets/icons/misc/vector.png
- assets/icons/misc/visible_on.png