From 221308b74bbb612208c7e5d043e41fbc8ae204c5 Mon Sep 17 00:00:00 2001 From: Jhalak Upadhyay <89909502+Jhalakupadhyay@users.noreply.github.com> Date: Sun, 20 Oct 2024 20:24:29 +0530 Subject: [PATCH] feat: Added the delete badge and import saved badge functionality. (#1047) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- assets/icons/empty_badge.svg | 24 ++++++ lib/bademagic_module/utils/file_helper.dart | 63 +++++++++++++++ lib/view/save_badge_screen.dart | 71 +++++++++++++++-- lib/view/saved_clipart.dart | 21 +++++ lib/view/widgets/badge_delete_dialog.dart | 88 +++++++++++++++++++++ lib/view/widgets/save_badge_card.dart | 25 +++++- lib/view/widgets/saved_badge_listview.dart | 14 +++- pubspec.lock | 16 ++++ pubspec.yaml | 1 + 9 files changed, 313 insertions(+), 10 deletions(-) create mode 100644 assets/icons/empty_badge.svg create mode 100644 lib/view/saved_clipart.dart create mode 100644 lib/view/widgets/badge_delete_dialog.dart diff --git a/assets/icons/empty_badge.svg b/assets/icons/empty_badge.svg new file mode 100644 index 00000000..3d447fb7 --- /dev/null +++ b/assets/icons/empty_badge.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/bademagic_module/utils/file_helper.dart b/lib/bademagic_module/utils/file_helper.dart index 35d09556..47b3ddfe 100644 --- a/lib/bademagic_module/utils/file_helper.dart +++ b/lib/bademagic_module/utils/file_helper.dart @@ -6,6 +6,8 @@ import 'package:badgemagic/bademagic_module/models/data.dart'; import 'package:badgemagic/bademagic_module/utils/byte_array_utils.dart'; import 'package:badgemagic/bademagic_module/utils/image_utils.dart'; import 'package:badgemagic/providers/imageprovider.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; import 'package:path_provider/path_provider.dart'; @@ -277,4 +279,65 @@ class FileHelper { logger.i('Error sharing file: $e'); } } + + Future deleteFile(String filename) async { + try { + final directory = await getApplicationDocumentsDirectory(); + final filePath = '${directory.path}/$filename'; + + File file = File(filePath); + if (await file.exists()) { + await file.delete(); + logger.i('File deleted: $filePath'); + } else { + logger.i('File not found: $filePath'); + } + } catch (e) { + logger.i('Error deleting file: $e'); + } + } + + Future importBadgeData(context) async { + try { + // Open file picker to select a JSON file + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['json'], // Only allow JSON files + ); + + if (result != null && result.files.isNotEmpty) { + // Get the selected file + File file = File(result.files.single.path!); + + // Read the content of the file + String jsonContent = await file.readAsString(); + + // Parse the JSON data + Map importedBadge = jsonDecode(jsonContent); + logger.d('Imported badge: $importedBadge'); + // Validate the structure of the JSON if necessary + if (importedBadge.containsKey('messages')) { + // Save the imported badge file to your application's directory + final directory = await getApplicationDocumentsDirectory(); + final filePath = '${directory.path}/${result.files.single.name}'; + logger.d('Importing badge to: $filePath'); + File newFile = File(filePath); + await newFile.writeAsString(jsonContent); + return true; + } else { + throw Exception("Invalid Badge Data"); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('No file selected')), + ); + return false; + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error importing badge: $e')), + ); + return false; + } + } } diff --git a/lib/view/save_badge_screen.dart b/lib/view/save_badge_screen.dart index aacfd7f7..ad9a9c7d 100644 --- a/lib/view/save_badge_screen.dart +++ b/lib/view/save_badge_screen.dart @@ -1,9 +1,16 @@ +import 'package:badgemagic/bademagic_module/utils/byte_array_utils.dart'; import 'package:badgemagic/bademagic_module/utils/file_helper.dart'; +import 'package:badgemagic/bademagic_module/utils/toast_utils.dart'; import 'package:badgemagic/constants.dart'; +import 'package:badgemagic/providers/badgeview_provider.dart'; import 'package:badgemagic/view/widgets/common_scaffold_widget.dart'; import 'package:badgemagic/view/widgets/saved_badge_listview.dart'; import 'package:badgemagic/virtualbadge/view/saved_badge_view.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get_it/get_it.dart'; class SaveBadgeScreen extends StatefulWidget { const SaveBadgeScreen({super.key}); @@ -14,43 +21,95 @@ class SaveBadgeScreen extends StatefulWidget { class _SaveBadgeScreenState extends State { List>> badgeData = []; + DrawBadgeProvider drawBadgeProvider = GetIt.instance(); + ToastUtils toastUtils = ToastUtils(); + FileHelper fileHelper = FileHelper(); @override void initState() { super.initState(); loadSavedBadges(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); + // Set a new 2D array to store the badge data with all false + drawBadgeProvider.setNewGrid( + List.generate(11, (index) => List.generate(44, (index) => false)), + true); } - void loadSavedBadges() async { + // Method to load saved badges and refresh the list + Future loadSavedBadges() async { var data = await fileHelper.getBadgeDataFiles(); setState(() { badgeData = data; }); } - FileHelper fileHelper = FileHelper(); @override Widget build(BuildContext context) { return CommonScaffold( actions: [ TextButton( - onPressed: () {}, + onPressed: () { + fileHelper.importBadgeData(context).then((value) { + if (value) { + logger.d('value: $value'); + toastUtils.showToast('Badge imported successfully'); + loadSavedBadges(); + } + }); + }, child: const Text( 'Import', style: TextStyle(color: Colors.white), )) ], body: badgeData.isEmpty - ? const Text("No data Available") + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(left: 50.0.w), + child: SvgPicture.asset( + 'assets/icons/empty_badge.svg', + height: 200.h, + ), + ), + SizedBox( + height: 20.h, + ), + Text( + 'No saved badges !', + style: TextStyle( + color: Colors.black, + fontSize: 20.sp, + ), + ), + Text( + 'Looks like there are no saved badges yet.', + style: TextStyle( + color: Colors.black, + fontSize: 14.sp, + ), + ), + ], + ), + ) : Column( children: [ SavedBadgeView(), BadgeListView( - futureBadges: fileHelper.getBadgeDataFiles(), // Fetch badges + futureBadges: Future.value(badgeData), + refreshBadgesCallback: loadSavedBadges, // Pass the callback ), ], ), - title: 'Bade Magic', + title: 'Badge Magic', key: const Key(drawBadgeScreen), ); } diff --git a/lib/view/saved_clipart.dart b/lib/view/saved_clipart.dart new file mode 100644 index 00000000..311c4d00 --- /dev/null +++ b/lib/view/saved_clipart.dart @@ -0,0 +1,21 @@ +import 'package:badgemagic/view/widgets/common_scaffold_widget.dart'; +import 'package:flutter/material.dart'; + +class SavedClipart extends StatefulWidget { + const SavedClipart({super.key}); + + @override + State createState() => _SavedClipartState(); +} + +class _SavedClipartState extends State { + @override + Widget build(BuildContext context) { + return CommonScaffold( + body: Column( + children: [], + ), + title: '', + ); + } +} diff --git a/lib/view/widgets/badge_delete_dialog.dart b/lib/view/widgets/badge_delete_dialog.dart new file mode 100644 index 00000000..f0e7c190 --- /dev/null +++ b/lib/view/widgets/badge_delete_dialog.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class DeleteBadgeDialog extends StatelessWidget { + const DeleteBadgeDialog({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.r), + ), + child: Container( + height: 110.h, + width: 300.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5.r), + ), + child: Column( + children: [ + SizedBox( + height: 10.h, + ), + Row( + children: [ + SizedBox( + width: 20, + ), + Icon(Icons.delete, color: Colors.black), + SizedBox( + width: 10, + ), + Text( + 'Delete', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + SizedBox( + height: 7.h, + ), + Row( + children: [ + SizedBox( + width: 20, + ), + Text('Are you sure want to delete this badge?', + style: TextStyle(fontSize: 14.sp)), + ], + ), + SizedBox( + height: 10.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: const Text( + 'Cancel', + style: TextStyle(color: Colors.red), + ), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: const Text( + 'OK', + style: TextStyle(color: Colors.red), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/widgets/save_badge_card.dart b/lib/view/widgets/save_badge_card.dart index 16d7a280..9dfe772e 100644 --- a/lib/view/widgets/save_badge_card.dart +++ b/lib/view/widgets/save_badge_card.dart @@ -5,17 +5,21 @@ import 'package:badgemagic/providers/badge_message_provider.dart'; import 'package:badgemagic/providers/badgeview_provider.dart'; import 'package:badgemagic/providers/cardsprovider.dart'; import 'package:badgemagic/view/draw_badge_screen.dart'; +import 'package:badgemagic/view/widgets/badge_delete_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; class SaveBadgeCard extends StatelessWidget { final MapEntry> badgeData; + final Future Function() refreshBadgesCallback; final FileHelper file = FileHelper(); final Converters converters = Converters(); + SaveBadgeCard({ super.key, required this.badgeData, + required this.refreshBadgesCallback, }); @override @@ -126,7 +130,17 @@ class SaveBadgeCard extends StatelessWidget { Icons.delete, color: Colors.black, ), - onPressed: () {}, + onPressed: () async { + // file.deleteFile(badgeData.key); + // refreshBadgesCallback(); + //add a dialog for confirmation before deleting + await _showDeleteDialog(context).then((value) async { + if (value == true) { + file.deleteFile(badgeData.key); + await refreshBadgesCallback(); + } + }); + }, ), ], ), @@ -272,4 +286,13 @@ class SaveBadgeCard extends StatelessWidget { ), ); } + + Future _showDeleteDialog(BuildContext context) async { + return await showDialog( + context: context, + builder: (BuildContext context) { + return DeleteBadgeDialog(); + }, + ); + } } diff --git a/lib/view/widgets/saved_badge_listview.dart b/lib/view/widgets/saved_badge_listview.dart index 0715aa13..838e43f8 100644 --- a/lib/view/widgets/saved_badge_listview.dart +++ b/lib/view/widgets/saved_badge_listview.dart @@ -3,13 +3,19 @@ import 'package:flutter/material.dart'; class BadgeListView extends StatelessWidget { final Future>>> futureBadges; + final Future Function() + refreshBadgesCallback; // Add callback for refreshing badges - const BadgeListView({super.key, required this.futureBadges}); + const BadgeListView( + {super.key, + required this.futureBadges, + required this.refreshBadgesCallback // Require the callback + }); @override Widget build(BuildContext context) { return FutureBuilder>>>( - future: futureBadges, // Pass the future + future: futureBadges, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); @@ -21,7 +27,9 @@ class BadgeListView extends StatelessWidget { itemCount: savedBadges.length, itemBuilder: (context, index) { return SaveBadgeCard( - badgeData: savedBadges[index], // Pass Data object to card + badgeData: savedBadges[index], + refreshBadgesCallback: + refreshBadgesCallback, // Pass callback to card ); }, ), diff --git a/pubspec.lock b/pubspec.lock index 05526d23..81dae441 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -145,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "167bb619cdddaa10ef2907609feb8a79c16dfa479d3afaf960f8e223f754bf12" + url: "https://pub.dev" + source: hosted + version: "8.1.2" fixnum: dependency: transitive description: @@ -179,6 +187,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" + url: "https://pub.dev" + source: hosted + version: "2.0.23" flutter_screenutil: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 81bb079a..6156f2e2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: path_provider: ^2.1.4 uuid: ^4.5.0 share_plus: ^10.0.3 + file_picker: ^8.1.2 dev_dependencies: flutter_test: