diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart index 1d31a2c4..4caa4731 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart @@ -1,8 +1,12 @@ +import 'dart:typed_data'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:file_saver/file_saver.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:go_router/go_router.dart'; +import 'package:ken_logger/ken_logger.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; @@ -16,8 +20,8 @@ import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; +import 'package:screenshot/screenshot.dart'; import 'package:supertokens_flutter/supertokens.dart'; -import 'package:url_launcher/url_launcher.dart'; class MihBusinessQrCode extends StatefulWidget { final Business business; @@ -38,6 +42,8 @@ class _MihBusinessQrCodeState extends State { late String qrCodedata; int qrSize = 500; bool _isUserSignedIn = false; + ScreenshotController screenshotController = ScreenshotController(); + Uint8List? businessQRImageFile; Future _checkUserSession() async { final doesSessionExist = await SuperTokens.doesSessionExist(); @@ -65,12 +71,88 @@ class _MihBusinessQrCodeState extends State { return "https://api.qrserver.com/v1/create-qr-code/?data=$encodedData&size=${qrSize}x${qrSize}&bgcolor=$bgColor&color=$color"; } + Future saveImage(Uint8List imageBytes) async { + final String filename = + "${widget.business.Name}_QR_Code_${DateTime.now().millisecondsSinceEpoch}.png"; + if (kIsWeb) { + await FileSaver.instance.saveFile( + name: filename, + bytes: imageBytes, + fileExtension: "png", + mimeType: MimeType.png, + ); + } else { + await FileSaver.instance.saveAs( + name: filename, + bytes: imageBytes, + fileExtension: "png", + mimeType: MimeType.png, + ); + } + // if (kIsWeb) { + // final blob = html.Blob([imageBytes]); + // final url = html.Url.createObjectUrlFromBlob(blob); + // final anchor = html.document.createElement('a') as html.AnchorElement + // ..href = url + // ..style.display = 'none' + // ..download = filename; // Suggested filename for the download + + // html.document.body!.children.add(anchor); + // anchor.click(); // Programmatically click the link to trigger download + + // html.document.body!.children.remove(anchor); + // html.Url.revokeObjectUrl(url); + // } else { + // var permission = await FlDownloader.requestPermission(); + // if (permission == StoragePermissionStatus.granted) { + // try { + // mihLoadingPopUp(); + // KenLogger.success("Downloading from URL: $url"); + // await FlDownloader.download(url); + // Navigator.of(context).pop(); + // } on Exception catch (error) { + // Navigator.of(context).pop(); + // print(error); + // } + // } else { + // print("denied"); + // } + // try { + // final directory = await getDownloadsDirectory(); + // final file = File('${directory?.path}/$filename'); + // await file.writeAsBytes(imageBytes); + // KenLogger.success("File saved at: ${file.path}"); + // } catch (e) { + // KenLogger.error("Error saving file: $e"); + // } + // } + } + + void mihLoadingPopUp() { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + } + Future downloadQrCode() async { if (_isUserSignedIn) { - final Uri uri = Uri.parse(getQrCodeData(1024)); - if (!await launchUrl(uri)) { - throw 'Could not launch $uri'; - } + // final Uri uri = Uri.parse(getQrCodeData(1024)); + // if (!await launchUrl(uri)) { + // throw 'Could not launch $uri'; + // } + await screenshotController.capture().then((image) { + KenLogger.success("Image Captured: $image"); + setState(() { + businessQRImageFile = image; + }); + }).catchError((onError) { + KenLogger.error(onError); + }); + KenLogger.success("QR Code Image Captured : $businessQRImageFile"); + saveImage(businessQRImageFile!); } else { showSignInRequiredAlert(); } @@ -134,6 +216,137 @@ class _MihBusinessQrCodeState extends State { ); } + Widget displayBusinessQRCode(double profilePictureWidth) { + return Screenshot( + controller: screenshotController, + child: Material( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark") + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(25), + elevation: 10, + shadowColor: Colors.black, + child: Container( + decoration: BoxDecoration( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(20), + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + FutureBuilder( + future: futureImageUrl, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == + ConnectionState.done && + asyncSnapshot.hasData) { + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + backgroundColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ); + } + } else { + return Icon( + MihIcons.mihRing, + size: profilePictureWidth, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ); + } + }, + ), + FittedBox( + child: Text( + widget.business.Name, + style: TextStyle( + fontSize: 35, + fontWeight: FontWeight.bold, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), + ), + FittedBox( + child: Text( + widget.business.type, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), + ), + const SizedBox(height: 5), + FittedBox( + child: Text( + "Powered by MIH", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), + ), + const SizedBox(height: 10), + SizedBox( + width: 300, + height: 300, + child: CachedNetworkImage( + imageUrl: getQrCodeData(qrSize.toInt()), + placeholder: (context, url) => const Mihloadingcircle(), + errorWidget: (context, url, error) => + const Icon(Icons.error), + ), + ), + const SizedBox(height: 10), + FittedBox( + child: Text( + "Scan & Connect", + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), + ), + ], + )), + ), + ), + ); + } + @override void dispose() { super.dispose(); @@ -175,137 +388,7 @@ class _MihBusinessQrCodeState extends State { horizontal: screenSize.width * 0), //.075), child: Padding( padding: const EdgeInsets.only(top: 10.0), - child: Material( - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark") - .withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(25), - elevation: 10, - shadowColor: Colors.black, - child: Container( - decoration: BoxDecoration( - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - borderRadius: BorderRadius.circular(20), - ), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - FutureBuilder( - future: futureImageUrl, - builder: (context, asyncSnapshot) { - if (asyncSnapshot.connectionState == - ConnectionState.done && - asyncSnapshot.hasData) { - if (asyncSnapshot.requireData != "") { - return MihCircleAvatar( - imageFile: NetworkImage( - asyncSnapshot.requireData), - width: profilePictureWidth, - editable: false, - fileNameController: - TextEditingController(), - userSelectedfile: file, - frameColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - backgroundColor: - MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - onChange: () {}, - ); - } else { - return Icon( - MihIcons.iDontKnow, - size: profilePictureWidth, - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - ); - } - } else { - return Icon( - MihIcons.mihRing, - size: profilePictureWidth, - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - ); - } - }, - ), - FittedBox( - child: Text( - widget.business.Name, - style: TextStyle( - fontSize: 35, - fontWeight: FontWeight.bold, - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - ), - ), - ), - FittedBox( - child: Text( - widget.business.type, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w600, - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - ), - ), - ), - const SizedBox(height: 10), - SizedBox( - width: 300, - height: 300, - child: CachedNetworkImage( - imageUrl: getQrCodeData(qrSize.toInt()), - placeholder: (context, url) => - const Mihloadingcircle(), - errorWidget: (context, url, error) => - const Icon(Icons.error), - ), - ), - const SizedBox(height: 10), - FittedBox( - child: Text( - "Scan & Connect", - style: TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - ), - ), - ), - ], - )), - ), - ), + child: displayBusinessQRCode(profilePictureWidth), ), ), ), diff --git a/Frontend/linux/flutter/generated_plugin_registrant.cc b/Frontend/linux/flutter/generated_plugin_registrant.cc index 2dccc220..88c7a8bd 100644 --- a/Frontend/linux/flutter/generated_plugin_registrant.cc +++ b/Frontend/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_saver_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); + file_saver_plugin_register_with_registrar(file_saver_registrar); g_autoptr(FlPluginRegistrar) printing_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin"); printing_plugin_register_with_registrar(printing_registrar); diff --git a/Frontend/linux/flutter/generated_plugins.cmake b/Frontend/linux/flutter/generated_plugins.cmake index 45f23698..561713c0 100644 --- a/Frontend/linux/flutter/generated_plugins.cmake +++ b/Frontend/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_saver printing url_launcher_linux ) diff --git a/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift b/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift index 16b81956..d79f29c2 100644 --- a/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import app_settings import device_info_plus import file_picker +import file_saver import flutter_tts import geolocator_apple import local_auth_darwin @@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppSettingsPlugin.register(with: registry.registrar(forPlugin: "AppSettingsPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) diff --git a/Frontend/pubspec.lock b/Frontend/pubspec.lock index 4a9b58fa..006b5171 100644 --- a/Frontend/pubspec.lock +++ b/Frontend/pubspec.lock @@ -417,6 +417,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.1.9" + file_saver: + dependency: "direct main" + description: + name: file_saver + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + url: "https://pub.dev" + source: hosted + version: "0.3.1" fixnum: dependency: transitive description: @@ -1232,6 +1240,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + screenshot: + dependency: "direct main" + description: + name: screenshot + sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b" + url: "https://pub.dev" + source: hosted + version: "3.0.0" scroll_to_index: dependency: transitive description: diff --git a/Frontend/pubspec.yaml b/Frontend/pubspec.yaml index f0e299f0..edc4c3bc 100644 --- a/Frontend/pubspec.yaml +++ b/Frontend/pubspec.yaml @@ -1,8 +1,8 @@ name: mzansi_innovation_hub description: "" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -# version: 1.2.0+97 -version: 1.1.0+97 #--- Updated version for upgrader package testing --- +version: 1.2.0+97 +# version: 1.1.1+97 #--- Updated version for upgrader package testing environment: sdk: '>=3.5.3 <4.0.0' @@ -54,6 +54,8 @@ dependencies: cached_network_image: ^3.4.1 gpt_markdown: ^1.1.2 upgrader: ^12.0.0 + screenshot: ^3.0.0 + file_saver: ^0.3.1 dev_dependencies: flutter_test: diff --git a/Frontend/windows/flutter/generated_plugin_registrant.cc b/Frontend/windows/flutter/generated_plugin_registrant.cc index 3632ed0b..9dbdf6de 100644 --- a/Frontend/windows/flutter/generated_plugin_registrant.cc +++ b/Frontend/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -17,6 +18,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSaverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSaverPlugin")); FlDownloaderPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlDownloaderPluginCApi")); FlutterTtsPluginRegisterWithRegistrar( diff --git a/Frontend/windows/flutter/generated_plugins.cmake b/Frontend/windows/flutter/generated_plugins.cmake index 6c32f4da..5d18711f 100644 --- a/Frontend/windows/flutter/generated_plugins.cmake +++ b/Frontend/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_saver fl_downloader flutter_tts geolocator_windows