quickcapture 1.1.12
quickcapture: ^1.1.12 copied to clipboard
QuickCapture - Ai Based Mobile Document Scanning,compression & imaging plugin for Flutter From Extrieve.
example/lib/main.dart
/// Sample Flutter application demonstrating the use of the QuickCapture Flutter plugin.
///
/// This application allows users to:
/// - Attach an image from the gallery.
/// - Capture document with SDK camera with ai features.
/// - Compress and optimize image with proper DPI, Layout control.
/// - Convert images to single TIFF and PDF files.
/// - Detect human faces in images and match them.
///
/// The application showcases how to integrate and use various features of the QuickCapture plugin.
///
/// Author: Extrieve Technologies
/// Website: https://www.extrieve.com
/// Extrieve Technologies - Your Expert in Document Management & AI Solutions
library;
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
import 'package:image_picker/image_picker.dart';
import 'package:open_file/open_file.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image/image.dart' as img;
import 'package:permission_handler/permission_handler.dart';
import 'package:geolocator/geolocator.dart';
// Import Quickcapture plugin
import 'package:quickcapture/quickcapture.dart';
void main() {
runApp(MaterialApp(navigatorKey: MyApp.navigatorKey, home: MyApp()));
}
class MyApp extends StatefulWidget {
// If you want to call showDialog without context, you can use this:
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
typedef MyCallback = void Function(String result);
class _MyAppState extends State<MyApp> {
bool isScreenProtectionEnabled = false;
bool isMonitoringEnabled = false;
final Quickcapture _quickcapturePlugin = Quickcapture();
// Store captured images from startCapture()
List<String> _capturedImage = [];
Uint8List? qrImage;
@override
void initState() {
super.initState();
requestLocationPermission();
initPlugin();
}
void initPlugin() async {
await _quickcapturePlugin.initialize();
await activateLicense();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('QuickCapture Sample Demo')),
body: SingleChildScrollView(
child: Column(
children: [
if (_capturedImage.isNotEmpty) ...[
const SizedBox(height: 10),
Text(
"Captured Images",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
SizedBox(
height: 180,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _capturedImage.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
FullImageView(imagePath: _capturedImage[index]),
),
);
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.file(
File(_capturedImage[index]),
width: 140,
fit: BoxFit.cover,
),
),
),
);
},
),
),
],
ElevatedButton(
onPressed: () => startCapture(),
child: const Text("Start Capture"),
),
ElevatedButton(
onPressed: () {
_showBottomStampDialog();
},
child: Text("Add Bottom Stamp"),
),
ElevatedButton(
onPressed: () => assetCapture(),
child: const Text("Asset Capture"),
),
ElevatedButton(
onPressed: () => buildPDFForLastCapture(),
child: const Text("Build PDF For Last Capture"),
),
ElevatedButton(
onPressed: () => buildTIFFForLastCapture(),
child: const Text("Build TIFF For Last Capture"),
),
// ElevatedButton(
// onPressed: () async {
// final ImagePicker picker = ImagePicker();
// final XFile? image = await picker.pickImage(
// source: ImageSource.gallery,
// );
// if (image != null) {
// compressImage(image.path);
// }
// },
// child: const Text("Pick Image & Compress"),
// ),
ElevatedButton(
onPressed: () => buildPDF(),
child: const Text("Build PDF from images"),
),
ElevatedButton(
onPressed: () => buildTIFF(),
child: const Text("Build TIFF from images"),
),
ElevatedButton(
onPressed: () => pickImageFromGallery(),
child: const Text("Attach from Gallery"),
),
ElevatedButton(
onPressed: () => pickAndDetectHumanFaces(),
child: const Text("Human Face Detection & Matching"),
),
ElevatedButton(
onPressed: () {
_showQrBarCodeDialog();
},
child: const Text("Generate QR / Bar Code"),
),
ElevatedButton(
onPressed: () async {
await startCodeScanner();
},
child: const Text("Scan QR/ Bar Code"),
),
ElevatedButton(
onPressed: () => getDeviceInfo(),
child: const Text("Get Device Info"),
),
ElevatedButton(
onPressed: () {
pickImageAndReadInfo();
},
child: Text("Read Image Info"),
),
ElevatedButton(
onPressed: () => getLocationInfo(),
child: const Text("Generate Location Information "),
),
ElevatedButton(
onPressed: () => getCurrentDigiPin(),
child: const Text(" Retrieves the current DigiPIN."),
),
ElevatedButton(
onPressed: () => getSecurityInfo(),
child: const Text("Get Security Info"),
),
Center(
child: SizedBox(
width: 300, // fixed width for alignment
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Center(
child: Text("Screen Protection"),
),
),
Switch(
value: isScreenProtectionEnabled,
onChanged: (value) async {
setState(() {
isScreenProtectionEnabled = value;
});
if(value){
// Enable screen protection to prevent screenshots and screen recording
await _quickcapturePlugin.enableScreenProtection();
}else{
// Disable screen protection
await _quickcapturePlugin.disableScreenProtection();
}
},
),
],
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Center(
child: Text("Device Monitoring"),
),
),
Switch(
value: isMonitoringEnabled,
onChanged: (value) async {
setState(() {
isMonitoringEnabled = value;
});
if(value){
// Start monitoring device
await _quickcapturePlugin.startDeviceMonitoring();
}else{
// Stop monitoring device
await _quickcapturePlugin.stopDeviceMonitoring();
}
},
),
],
),
],
),
),
)
],
),
),
);
}
/// Activate Quickcapture license
Future<void> activateLicense() async {
// TODO: Securely fetch license keys from a secure source.
String androidLicense =
"<Pass the license string for Android platform here>";
String iosLicense = "<Pass the license string for IOS platform here>";
bool? response = await _quickcapturePlugin.activateLicense(
android: androidLicense,
ios: iosLicense,
);
String lisMsg = "License activation failed. Invalid license";
Color bgColor = const Color.fromARGB(255, 216, 90, 58);
if (response == true) {
lisMsg = "License for android activated";
bgColor = const Color.fromARGB(255, 58, 216, 84);
}
Fluttertoast.showToast(
msg: lisMsg,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: bgColor,
textColor: Colors.white,
fontSize: 16.0,
);
}
/// Set config for capturing images with QuickCapture
void setConfigForCapture() {
// Example: Set DPI and layout type
_quickcapturePlugin.config.image.setDPI(DPI.dpi200);
_quickcapturePlugin.config.image.setLayoutType(LayoutType.A4);
_quickcapturePlugin.config.image.setResizeMode(
ResizeMode.preserveAspectOnly,
);
// Enable flash, set max pages, etc.
_quickcapturePlugin.config.capture.enableFlash = true;
_quickcapturePlugin.config.capture.captureSound = false;
_quickcapturePlugin.config.capture.maxPage = 0;
// Enable torch on low light conditions
_quickcapturePlugin.config.capture.enableTorchOnLowLight = true;
// Enable auto-correct document angle in review screen
_quickcapturePlugin.config.review.enableAutoCorrectDocAngle = true;
}
Future<void> addBottomStamp(String text) async {
try {
// Get location info and DigiPIN to include in the bottom stamp
String? location = await _quickcapturePlugin.getLocationInfo();
if(location == null){
Fluttertoast.showToast(
msg: "Unable to fetch location info",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
return;
}
print(location);
Map<String, dynamic> jsonMap = json.decode(location);
String finalText = text + "\$ Latitude : " + jsonMap['Latitude'].toString() + " Longitude : " + jsonMap['Longitude'].toString() + " DigiPIN : " + jsonMap['DigiPIN'].toString();
_quickcapturePlugin.config.capture.bottomStampData = finalText;
Fluttertoast.showToast(
msg: "Bottom Stamping Set Successfully, Now start Asset capture/document capture to see the stamping effect.",
backgroundColor: const Color.fromARGB(255, 169, 202, 171),
textColor: Colors.white,
);
} catch (e) {
Fluttertoast.showToast(
msg: "Stamp Error: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
/// Start capturing images using QuickCapture
Future<void> startCapture() async {
try {
setConfigForCapture();
String? response = await _quickcapturePlugin.startCapture();
if (response != null) {
setState(() {
Map<String, dynamic> jsonResponse = jsonDecode(response);
_capturedImage = List<String>.from(jsonResponse['fileCollection']);
});
// Save to gallery
for (String imagePath in _capturedImage) {
final result = await ImageGallerySaverPlus.saveFile(imagePath);
if (result == true) {
Fluttertoast.showToast(
msg: "Image saved to gallery: $imagePath",
backgroundColor: Colors.green,
textColor: Colors.white,
);
} else {
Fluttertoast.showToast(
msg: "Failed to save image: $imagePath",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
} else {
throw Exception("No response received from startCapture");
}
} catch (e) {
Fluttertoast.showToast(
msg: "Capture failed",
backgroundColor: Colors.red,
);
}
}
//Asset capture
Future<void> assetCapture() async {
try {
String? response = await _quickcapturePlugin.assetCapture();
if (response != null) {
Map<String, dynamic> jsonResponse = jsonDecode(response);
List<String> capturedImages = List<String>.from(
jsonResponse['fileCollection'],
);
setState(() {
_capturedImage = (capturedImages);
});
} else {
Fluttertoast.showToast(
msg: "No images captured",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error in asset capture: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
/// Build PDF from last capture
Future<void> buildPDFForLastCapture() async {
String? response = await _quickcapturePlugin.buildPDFForLastCapture();
if (response != null) {
OpenFile.open(response);
}
}
/// Build TIFF from last capture
Future<void> buildTIFFForLastCapture() async {
String? response = await _quickcapturePlugin.buildTIFFForLastCapture();
if (response != null) {
OpenFile.open(response);
}
}
//Pick image and compress
Future<void> compressImage(String imagePath) async {
try {
String? compressedImagePath = await _quickcapturePlugin.compressToJPEG(
imagePath,
);
if (compressedImagePath != null) {
print("Compressed image: $compressedImagePath");
await ImageGallerySaverPlus.saveFile(compressedImagePath);
setState(() {
_capturedImage.add(compressedImagePath);
});
Fluttertoast.showToast(
msg: "Compressed Image Saved",
backgroundColor: Colors.green,
textColor: Colors.white,
);
} else {
print("Compression failed");
}
} catch (e) {
print("Error compressing image: $e");
}
}
/// Build PDF from selected images
Future<void> buildPDF() async {
final ImagePicker picker = ImagePicker();
try {
final List<XFile> images = await picker.pickMultiImage();
if (images.isEmpty) {
Fluttertoast.showToast(
msg: "No images selected.",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
return;
}
final List<String> imagePaths = images
.map((image) => image.path)
.toList();
final String? response = await _quickcapturePlugin.buildPDF(imagePaths);
if (response != null) {
OpenFile.open(response);
} else {
Fluttertoast.showToast(
msg: "Failed to create PDF",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error building PDF: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
/// Build TIFF from selected images
Future<void> buildTIFF() async {
final ImagePicker picker = ImagePicker();
try {
final List<XFile> images = await picker.pickMultiImage();
if (images.isEmpty) {
Fluttertoast.showToast(
msg: "No images selected.",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
return;
}
final List<String> imagePaths = images
.map((image) => image.path)
.toList();
final String? response = await _quickcapturePlugin.buildTiff(imagePaths);
if (response != null) {
print("TIFF saved at: $response");
Fluttertoast.showToast(
msg: "TIFF Created Successfully",
backgroundColor: Colors.green,
textColor: Colors.white,
);
OpenFile.open(response);
} else {
Fluttertoast.showToast(
msg: "Failed to create TIFF",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error building TIFF: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
final ImagePicker _picker = ImagePicker();
/// Pick image from Gallery, apply QuickCapture compression & optimisation based on imaging config
Future<void> pickImageFromGallery() async {
final ImagePicker picker = ImagePicker();
try {
final XFile? pickedFile = await picker.pickImage(
source: ImageSource.gallery,
);
if (pickedFile != null) {
String imagePath = pickedFile.path;
if (kDebugMode) {
print("imagePath:$imagePath");
}
// Example: set resizing mode
_quickcapturePlugin.config.image.setResizeMode(
ResizeMode.preserveAspectOnly,
);
//_quickcapturePlugin.config.image.setLayoutType(LayoutType.A4);
//_quickcapturePlugin.config.image.setCustomLayout(2, 1);
_quickcapturePlugin.config.review.enableAutoCorrectDocAngle = true;
String? response = await _quickcapturePlugin.startImageAttach(
imagePath,
);
if (response != null) {
setState(() {
Map<String, dynamic> jsonResponse = jsonDecode(response);
List<String> images = List<String>.from(
jsonResponse['fileCollection'],
);
_capturedImage = (images);
});
// Save to gallery
for (String imagePath in _capturedImage) {
final result = await ImageGallerySaverPlus.saveFile(imagePath);
if (result == true) {
Fluttertoast.showToast(
msg: "Image saved to gallery: $imagePath",
backgroundColor: Colors.green,
textColor: Colors.white,
);
} else {
Fluttertoast.showToast(
msg: "Failed to save image: $imagePath",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
} else {
throw Exception("No response received from startImageAttach");
}
} else {
Fluttertoast.showToast(
msg: "No image selected.",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error picking or processing image: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
} /// Pick image, detect faces, and show a popup for multiple images with face matching
Future<void> pickAndDetectHumanFaces() async {
final ImagePicker picker = ImagePicker();
List<Map<String, dynamic>> attachedImages = [];
List<Map<String, dynamic>> allFaceThumbnails = [];
List<Map<String, dynamic>> selectedThumbnails = [];
void handleFaceThumbnailSelection(Map<String, dynamic> thumbnail) {
if (selectedThumbnails.contains(thumbnail)) {
selectedThumbnails.remove(thumbnail);
} else {
if (selectedThumbnails.length < 2) {
selectedThumbnails.add(thumbnail);
} else {
Fluttertoast.showToast(
msg: "Only 2 thumbnails can be selected at a time.",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
}
}
}
void showPopup() {
final ctx = MyApp.navigatorKey.currentContext ?? context;
showDialog(
context: ctx,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text("Detect and Match Faces"),
content: SingleChildScrollView(
child: Column(
children: [
// Display attached images
attachedImages.isNotEmpty
? Column(
children: attachedImages
.map(
(imageData) => Image.file(
File(imageData['path']),
width: 100,
height: 100,
),
)
.toList(),
)
: const Text("No images attached"),
ElevatedButton(
onPressed: () async {
final XFile? pickedFile = await picker.pickImage(
source: ImageSource.gallery,
);
if (pickedFile != null) {
setState(() {
attachedImages.add({
'path': pickedFile.path,
'docId': null,
});
});
Fluttertoast.showToast(
msg: "Image attached. Detecting faces...",
backgroundColor: Colors.blue,
textColor: Colors.white,
);
String? detectionResponse =
await _quickcapturePlugin.detectHumanFaces(
pickedFile.path,
);
if (detectionResponse != null &&
detectionResponse.isNotEmpty) {
Map<String, dynamic> responseJson = jsonDecode(
detectionResponse,
);
if (responseJson["STATUS"] == true &&
responseJson["DATA"] != null) {
List<dynamic> faceData = responseJson["DATA"];
final originalBytes = await File(
pickedFile.path,
).readAsBytes();
final img.Image? originalImage = img
.decodeImage(originalBytes);
if (originalImage != null) {
for (var face in faceData) {
int left = face["LEFT"].clamp(
0,
originalImage.width,
);
int right = face["RIGHT"].clamp(
0,
originalImage.width,
);
int top = face["TOP"].clamp(
0,
originalImage.height,
);
int bottom = face["BOTTOM"].clamp(
0,
originalImage.height,
);
final croppedFace = img.copyCrop(
originalImage,
x: left,
y: top,
width: right - left,
height: bottom - top,
);
final croppedBytes = img.encodeJpg(
croppedFace,
);
setState(() {
allFaceThumbnails.add({
'thumbnail': Uint8List.fromList(
croppedBytes,
),
'docId': responseJson['IDENTIFIER'],
'faceIndex': face['INDEX'],
});
});
}
}
}
}
}
},
child: const Text("Attach Image"),
),
const SizedBox(height: 16),
// Show face thumbnails
allFaceThumbnails.isNotEmpty
? Wrap(
spacing: 8,
runSpacing: 8,
children: allFaceThumbnails.map((thumbnailData) {
return GestureDetector(
onTap: () {
setState(() {
handleFaceThumbnailSelection(
thumbnailData,
);
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color:
selectedThumbnails.contains(
thumbnailData,
)
? Colors.blue
: Colors.transparent,
width: 2,
),
),
child: Image.memory(
thumbnailData['thumbnail'],
width: 80,
height: 80,
fit: BoxFit.cover,
),
),
);
}).toList(),
)
: const Text("No faces detected"),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Close"),
),
if (selectedThumbnails.length == 2)
ElevatedButton(
onPressed: () async {
Fluttertoast.showToast(
msg: "Matching faces...",
backgroundColor: Colors.blue,
textColor: Colors.white,
);
String? matchResult = await _quickcapturePlugin
.matchHumanFaces(
firstDocumentID: selectedThumbnails[0]['docId'],
firstDocumentFaceIndex:
selectedThumbnails[0]['faceIndex'],
secondDocumentID: selectedThumbnails[1]['docId'],
secondDocumentFaceIndex:
selectedThumbnails[1]['faceIndex'],
);
Fluttertoast.showToast(
msg: "Match result: $matchResult",
backgroundColor: Colors.green,
textColor: Colors.white,
);
},
child: const Text("Match Faces"),
),
],
);
},
);
},
);
}
// Call `initHumanFaceHelper` and show the popup
bool? initialized = await initHumanFaceHelper();
if (initialized == true) {
showPopup();
} else {
Fluttertoast.showToast(
msg: "Failed to initialize Human Face Helper.",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
/// Initialize Human Face Helper
Future<bool?> initHumanFaceHelper() async {
bool? result = await _quickcapturePlugin.initHumanFaceHelper();
String message = result == true
? "Human Face Helper initialized successfully."
: "Failed to initialize Human Face Helper.";
Fluttertoast.showToast(
msg: message,
backgroundColor: result == true ? Colors.green : Colors.red,
textColor: Colors.white,
);
return result;
}
//
void _showBottomStampDialog() {
final TextEditingController stampController = TextEditingController();
showDialog(
context: MyApp.navigatorKey.currentState!.overlay!.context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Add Bottom Stamp"),
content: TextField(
controller: stampController,
maxLines: 2,
decoration: const InputDecoration(
hintText: "{DATETIME} , Other info \$ next line info....",
border: OutlineInputBorder(),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () {
final String stampText = stampController.text.trim();
if (stampText.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Please enter some text")),
);
return;
}
addBottomStamp(stampText);
Navigator.of(context).pop();
},
child: const Text("Submit"),
),
],
);
},
);
}
Future<void> generateQRCode(String data) async {
try {
String? response = await _quickcapturePlugin.generateQRCode(data);
print("QR RESPONSE: $response");
if (response != null && response.isNotEmpty) {
setState(() {
qrImage = base64Decode(response);
});
}
} catch (e) {
print("Error Generating QR Code: $e");
}
}
/// Generate a bar code from the provided data
Future<void> generateBARCode(String data) async {
try {
String? response = await _quickcapturePlugin.generateBARCode(data, true);
print("BARCODE RESPONSE: $response");
if (response != null && response.isNotEmpty) {
setState(() {
qrImage = base64Decode(response);
});
Fluttertoast.showToast(
msg: "Bar code generated successfully",
backgroundColor: Colors.green,
textColor: Colors.white,
);
}
} catch (e) {
print("Error generating bar code: $e");
}
}
/// Get device information
Future<void> getDeviceInfo() async {
try {
String? response = await _quickcapturePlugin.getDeviceInfo();
if (response != null) {
Fluttertoast.showToast(
msg: "Device info: $response",
backgroundColor: Colors.green,
textColor: Colors.white,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error getting device info: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
);
}
}
Future<void> pickImageAndReadInfo() async {
try {
// Image select from gallery
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image == null) {
print("No image selected");
return;
}
String imagePath = image.path;
print("Selected Image Path: $imagePath");
// Read image info
String? response = await _quickcapturePlugin.readImageInfo(imagePath);
print("Image Info: $response");
Fluttertoast.showToast(
msg: response ?? "No response",
backgroundColor: Colors.green,
textColor: Colors.white,
);
} catch (e) {
print("Error: $e");
}
}
Future<void> getLocationInfo() async {
try{
String? response = await _quickcapturePlugin.getLocationInfo();
if (response != null) {
Fluttertoast.showToast(
msg: "Location Info: $response",
backgroundColor: Colors.blue,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error getting location info: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
}
// GEt current DigiPin
Future<void> getCurrentDigiPin() async {
try{
String? response = await _quickcapturePlugin.getCurrentDigiPin();
if (response != null) {
Fluttertoast.showToast(
msg: "Current DigiPin: $response",
backgroundColor: Colors.blue,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error getting DigiPin: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
}
Future<void> requestLocationPermission() async {
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.deniedForever) {
await Geolocator.openAppSettings();
return;
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
}
Future<void> startImageAttach(String imagePath) async {
try {
String? response = await _quickcapturePlugin.startImageAttach(imagePath);
if (response != null) {
Map<String, dynamic> jsonResponse = jsonDecode(response);
List<String> capturedImages = List<String>.from(
jsonResponse['fileCollection'],
);
setState(() {
_capturedImage = capturedImages;
});
print("Captured Images: $capturedImages");
} else {
print("Image attach cancelled.");
}
} catch (e) {
print("Error attaching image: $e");
}
}
// Get security information
Future<void> getSecurityInfo() async {
try{
String? response = await _quickcapturePlugin.getSecurityInfo();
if (response != null) {
Fluttertoast.showToast(
msg: "Security Info: $response",
backgroundColor: Colors.blue,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
} catch (e) {
Fluttertoast.showToast(
msg: "Error getting security info: ${e.toString()}",
backgroundColor: Colors.red,
textColor: Colors.white,
toastLength: Toast.LENGTH_LONG,
);
}
}
/// Show dialog to input barcode data and generate barcode
void showBarcodeInputDialog() {
final TextEditingController controller = TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Generate BAR Code"),
content: TextField(
controller: controller,
decoration: const InputDecoration(hintText: "Enter barcode data"),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () {
if (controller.text.isNotEmpty) {
generateBARCode(controller.text);
Navigator.of(context).pop();
} else {
Fluttertoast.showToast(
msg: "Please enter barcode data",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
}
},
child: const Text("Generate"),
),
],
);
},
);
}
void showCodeOptions() {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: Icon(Icons.qr_code),
title: Text("Generate QR Code"),
onTap: () {
Navigator.pop(context);
showQRInputDialog();
},
),
ListTile(
leading: Icon(Icons.qr_code_2),
title: Text("Generate Barcode"),
onTap: () {
Navigator.pop(context);
showBarcodeInputDialog();
},
),
// 🔥 NEW OPTION
ListTile(
leading: Icon(Icons.camera_alt),
title: Text("Scan Code"),
onTap: () {
Navigator.pop(context);
startCodeScanner();
},
),
],
);
},
);
}
//
void showQRInputDialog() {
TextEditingController qrController = TextEditingController();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Enter text for QR Code"),
content: TextField(
controller: qrController,
decoration: const InputDecoration(hintText: "Enter text"),
),
actions: [
ElevatedButton(
onPressed: () async {
String text = qrController.text;
if (text.isNotEmpty) {
await generateQRCode(text);
Navigator.pop(context);
} else {
Fluttertoast.showToast(
msg: "Enter text first",
backgroundColor: Colors.orange,
textColor: Colors.white,
);
}
},
child: const Text("Submit"),
),
],
);
},
);
}
Future<void> startCodeScanner() async {
try {
// Start scanning
String? response = await _quickcapturePlugin.startOpticalCodeCapture();
if (response != null && response.isNotEmpty) {
Fluttertoast.showToast(
msg: "Scanned: $response",
backgroundColor: Colors.green,
textColor: Colors.white,
);
} else {
Fluttertoast.showToast(msg: "No code detected");
}
} catch (e) {
print("SCAN ERROR: $e");
Fluttertoast.showToast(msg: "Scan failed", backgroundColor: Colors.red);
}
}
Future<String?> buildQRCode(String data) async {
try {
String? response = await _quickcapturePlugin.generateQRCode(data);
return response;
} catch (e) {
print("QR Error: $e");
return null;
}
}
Future<String?> buildBARCode(String data, bool showText) async {
try {
String? response = await _quickcapturePlugin.generateBARCode(
data,
showText,
);
return response;
} catch (e) {
print("BAR Error: $e");
return null;
}
}
void _showQrBarCodeDialog() {
final TextEditingController qrDataController = TextEditingController();
String? base64Image;
showDialog(
context: MyApp.navigatorKey.currentState!.overlay!.context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text("Build QR/Bar Code"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: qrDataController,
maxLines: 4,
decoration: const InputDecoration(
hintText: "Enter data for QR/Bar code...",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
// IMAGE PREVIEW
if (base64Image != null)
SizedBox(
height: 200,
child: Image.memory(
base64Decode(base64Image!.split(',').last),
fit: BoxFit.contain,
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Close"),
),
ElevatedButton(
onPressed: () async {
FocusManager.instance.primaryFocus?.unfocus();
final qrData = qrDataController.text.trim();
if (qrData.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Please enter some data")),
);
return;
}
// Generate QR code
final result = await buildQRCode(qrData); // base64
setState(() {
base64Image = result;
});
},
child: const Text("Generate QR Code"),
),
ElevatedButton(
onPressed: () async {
FocusManager.instance.primaryFocus?.unfocus();
final qrData = qrDataController.text.trim();
if (qrData.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Please enter some data")),
);
return;
}
// Generate bar code without text
final result = await buildBARCode(qrData, false);
setState(() {
base64Image = result;
});
},
child: const Text("Generate Bar Code"),
),
ElevatedButton(
onPressed: () async {
FocusManager.instance.primaryFocus?.unfocus();
final qrData = qrDataController.text.trim();
if (qrData.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Please enter some data")),
);
return;
}
// Generate bar code with text
final result = await buildBARCode(qrData, true);
setState(() {
base64Image = result;
});
},
child: const Text("Generate Bar Code with Text"),
),
],
);
},
);
},
);
} //
}
class FullImageView extends StatelessWidget {
final String imagePath;
const FullImageView({super.key, required this.imagePath});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Captured Image")),
body: Center(child: Image.file(File(imagePath))),
);
}
}
/// Basic widget to display a list of image paths in a grid
class ImageGrid extends StatelessWidget {
final List<String> imagePaths;
const ImageGrid(this.imagePaths, {super.key});
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: imagePaths.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
FullImageView(imagePath: imagePaths[index]),
),
);
},
child: Image.file(File(imagePaths[index]), fit: BoxFit.cover),
);
},
);
}
}