From 64b90b3c9d15a22201fadffb8f880aa43bc23ac8 Mon Sep 17 00:00:00 2001 From: venkatbalajim Date: Thu, 7 Mar 2024 16:37:38 +0530 Subject: [PATCH] Bug fixes and improvements --- lib/main.dart | 15 ++++++++ lib/pages/generator_page.dart | 8 ++-- lib/pages/history_page.dart | 2 +- lib/pages/home_page.dart | 2 +- lib/pages/result_page.dart | 17 +++++--- lib/pages/scanner_page.dart | 1 + lib/utils/download.dart | 33 ++++++++++------ lib/utils/history.dart | 45 +++++++++------------- lib/utils/imports.dart | 2 + lib/utils/themes.dart | 10 +++-- lib/widgets/generator_page/text_field.dart | 24 ++++++++++++ 11 files changed, 107 insertions(+), 52 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fa6ff2d..8c62a19 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'utils/imports.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); runApp( ChangeNotifierProvider( create: (_) => ThemeProvider(), @@ -14,6 +15,8 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + _requestPermissions(); + return MaterialApp( title: 'QR Hub', debugShowCheckedModeBanner: false, @@ -30,4 +33,16 @@ class MyApp extends StatelessWidget { home: const HomePage(), ); } + + Future _requestPermissions() async { + Map statuses = await [ + Permission.camera, + Permission.storage, + ].request(); + + if (statuses[Permission.camera] != PermissionStatus.granted || + statuses[Permission.storage] != PermissionStatus.granted) { + + } + } } diff --git a/lib/pages/generator_page.dart b/lib/pages/generator_page.dart index 6f21dd8..666cbc4 100644 --- a/lib/pages/generator_page.dart +++ b/lib/pages/generator_page.dart @@ -45,7 +45,7 @@ class _GeneratorPageState extends State { context: context, builder: (context) { return AlertDialog( - title: const Text('Empty Data'), + title: Text('Empty Data', style: Theme.of(context).textTheme.headlineMedium), content: const Text('Kindly enter important data to generate QR Code.'), actions: [ TextButton( @@ -376,7 +376,9 @@ END:VEVENT value: selectedEncryption, onChanged: (String? newValue) { if (newValue != null) { - selectedEncryption = newValue; + setState(() { + selectedEncryption = newValue; + }); } }, items: wifiEncryptionOptions.map((String option) { @@ -395,7 +397,7 @@ END:VEVENT child: CustomTextButton( buttonName: 'Generate QR Code', onPressed: () { - if (qrDataController.text.isEmpty || subjectController.text.isEmpty) { + if (qrDataController.text.isEmpty) { showEmptyDataDialog(context); } else { setState(() { diff --git a/lib/pages/history_page.dart b/lib/pages/history_page.dart index f4eede0..615fcad 100644 --- a/lib/pages/history_page.dart +++ b/lib/pages/history_page.dart @@ -202,7 +202,7 @@ ${data.qrCodeData} context: context, builder: (context) { return AlertDialog( - title: Text("${data.dataType} QR Code"), + title: Text("${data.dataType} QR Code", style: Theme.of(context).textTheme.headlineMedium,), content: Text(contentData), actions: [ Row( diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 06e4585..e8de231 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -49,7 +49,7 @@ class _HomePageState extends State { Text( "QR Hub", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headlineMedium, + style: Theme.of(context).textTheme.headlineLarge, ), const SizedBox(height: 20), Text( diff --git a/lib/pages/result_page.dart b/lib/pages/result_page.dart index ba75049..6bdf997 100644 --- a/lib/pages/result_page.dart +++ b/lib/pages/result_page.dart @@ -3,6 +3,7 @@ import '../utils/imports.dart'; class ResultPage extends StatefulWidget { final Barcode value; final Uint8List? image; + final MobileScannerController controller; final Function()? screenClose; final String? scannedDate; final String? scannedTime; @@ -12,7 +13,8 @@ class ResultPage extends StatefulWidget { super.key, required this.value, this.screenClose, this.scannedDate, this.scannedTime, - this.dataType, required this.image + this.dataType, this.image, + required this.controller }); @override @@ -35,6 +37,8 @@ class _ResultPageState extends State { } void parseData() { + ValueNotifier torchStatus = widget.controller.torchState; + if (torchStatus.value == TorchState.on) widget.controller.toggleTorch(); Barcode fetchData = widget.value; value = fetchData; result = value.rawValue; @@ -116,11 +120,12 @@ class _ResultPageState extends State { const SizedBox(height: 20), ResultContainer(data: value), const SizedBox(height: 20), - SizedBox( - width: 250, - height: 250, - child: Image.memory(widget.image!), - ) + if (widget.image != null) + SizedBox( + width: 250, + height: 250, + child: Image.memory(widget.image!), + ) ], ), ) diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index 5bb6b98..2e7a775 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -131,6 +131,7 @@ class _ScannerPageState extends State { Navigator.push(context, MaterialPageRoute( builder: (context) => ResultPage( + controller: cameraController, value: code.barcodes.first, screenClose: screenClosed, image: image, diff --git a/lib/utils/download.dart b/lib/utils/download.dart index d0deba8..498dfce 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -11,17 +11,26 @@ Future downloadQRCode( Uint8List? image = await screenshotController.capture(); if (image != null) { - String downloadDir = (await DownloadsPath.downloadsDirectory())!.path; - String appFolderName = "QR Hub"; - String downloadFolder = "QR Codes"; - String fileName = DateFormat('yyyyMMddTHHmmss').format(DateTime.now()); - Directory directory = Directory('$downloadDir/$appFolderName/$downloadFolder'); - if (!directory.existsSync()) { - directory.createSync(); + if (await Permission.storage.isGranted) { + String downloadDir = (await DownloadsPath.downloadsDirectory())!.path; + String appFolderName = "QR Hub"; + String downloadFolder = "QR Codes"; + String fileName = DateFormat('yyyyMMddTHHmmss').format(DateTime.now()); + Directory directory = Directory('$downloadDir/$appFolderName/$downloadFolder'); + if (!directory.existsSync()) { + directory.createSync(recursive: true); + } + String filePath = "${directory.path}/$fileName.jpg"; + await File(filePath).writeAsBytes(image) + .then((value) => SnackBarWidget(context, 'File downloaded successfully.\nFile directory: Download/QR Hub/QR Codes')); + } else if (await Permission.storage.isDenied) { + await Permission.storage.request(); + if (await Permission.storage.isGranted) { + SnackBarWidget(context, 'Permission granted. Click the download button again.'); + } + } else if (await Permission.storage.isPermanentlyDenied) { + SnackBarWidget(context, 'Storage Permission Denied. So, unable to download the history.'); } - String filePath = "${directory.path}/$fileName.jpg"; - await File(filePath).writeAsBytes(image) - .then((value) => SnackBarWidget(context, 'File downloaded successfully.\nFile directory: Download/QR Hub/QR Codes')); } else { SnackBarWidget(context, 'Sorry, cannot able to fetch QR code image.'); } @@ -46,7 +55,7 @@ Future downloadHistory(BuildContext context, List historyList) String appFolderName = "QR Hub"; Directory directory = Directory('$downloadDir/$appFolderName/History'); if (!directory.existsSync()) { - directory.createSync(); + directory.createSync(recursive: true); } String fileName = DateFormat('yyyyMMddTHHmmss').format(DateTime.now()); String filePath = "${directory.path}/$fileName.txt"; @@ -60,7 +69,7 @@ Future downloadHistory(BuildContext context, List historyList) } else if (await Permission.storage.isDenied) { await Permission.storage.request(); if (await Permission.storage.isGranted) { - SnackBarWidget(context, 'Permission granted. Click the download button again'); + SnackBarWidget(context, 'Permission granted. Click the download button again.'); } } else if (await Permission.storage.isPermanentlyDenied) { SnackBarWidget(context, 'Storage Permission Denied. So, unable to download the history.'); diff --git a/lib/utils/history.dart b/lib/utils/history.dart index b1692db..ff2c9b9 100644 --- a/lib/utils/history.dart +++ b/lib/utils/history.dart @@ -4,7 +4,7 @@ class QRCodeData { String scannedDate; String scannedTime; String dataType; - String qrCodeData; + String qrCodeData; QRCodeData({ required this.scannedDate, @@ -13,21 +13,21 @@ class QRCodeData { required this.qrCodeData, }); - Map toJson() { - return { - 'scan_date': scannedDate, - 'scan_time': scannedTime, - 'qr_code_type': dataType, - 'qr_code_data': qrCodeData, - }; + List toList() { + return [ + scannedDate, + scannedTime, + dataType, + qrCodeData, + ]; } - factory QRCodeData.fromJson(Map json) { + factory QRCodeData.fromList(List list) { return QRCodeData( - scannedDate: json['scan_date'] ?? '', - scannedTime: json['scan_time'] ?? '', - dataType: json['qr_code_type'] ?? '', - qrCodeData: json['qr_code_data'] ?? '', + scannedDate: list[0] ?? '', + scannedTime: list[1] ?? '', + dataType: list[2] ?? '', + qrCodeData: list[3] ?? const Barcode(), ); } } @@ -39,15 +39,10 @@ class QRCodeHistoryManager { SharedPreferences prefs = await SharedPreferences.getInstance(); final historyJson = prefs.getString(_key); if (historyJson != null) { - final List> historyList = - (json.decode(historyJson) as List).cast>(); - final List> castedHistoryList = - historyList.map((item) { - return item.cast(); - }).toList(growable: false); - - return castedHistoryList - .map((item) => QRCodeData.fromJson(item)) + final List> historyList = + (json.decode(historyJson) as List).cast>(); + return historyList + .map((item) => QRCodeData.fromList(item)) .toList(growable: true); } return []; @@ -57,15 +52,13 @@ class QRCodeHistoryManager { SharedPreferences prefs = await SharedPreferences.getInstance(); final history = await getHistory(); history.add(data); - final historyJson = - json.encode(history.map((item) => item.toJson()).toList()); + final historyJson = json.encode(history.map((item) => item.toList()).toList()); prefs.setString(_key, historyJson); } Future saveHistory(List history) async { SharedPreferences prefs = await SharedPreferences.getInstance(); - final historyJson = - json.encode(history.map((item) => item.toJson()).toList()); + final historyJson = json.encode(history.map((item) => item.toList()).toList()); prefs.setString(_key, historyJson); } } diff --git a/lib/utils/imports.dart b/lib/utils/imports.dart index 6700039..82fcd39 100644 --- a/lib/utils/imports.dart +++ b/lib/utils/imports.dart @@ -130,6 +130,8 @@ import 'package:flutter/material.dart'; export 'package:flutter/material.dart'; import 'package:flutter/services.dart'; export 'package:flutter/services.dart'; +import 'package:flutter/cupertino.dart'; +export 'package:flutter/cupertino.dart' hide RefreshCallback; // QR Generator Package import 'package:qr_flutter/qr_flutter.dart'; diff --git a/lib/utils/themes.dart b/lib/utils/themes.dart index a2eada8..6c6c4dd 100644 --- a/lib/utils/themes.dart +++ b/lib/utils/themes.dart @@ -6,7 +6,8 @@ ThemeData lightTheme = ThemeData( brightness: Brightness.light, useMaterial3: true, textTheme: TextTheme( - headlineMedium: TextStyle(fontSize: 50, color: Colors.blue[900]), + headlineLarge: TextStyle(fontSize: 50, color: Colors.blue[900]), + headlineMedium: const TextStyle(fontSize: 25, color: Colors.black), headlineSmall: TextStyle(fontSize: 17, color: Colors.grey[600]), labelSmall: TextStyle( fontSize: 15, color: Colors.blue[900], fontWeight: FontWeight.w400 @@ -47,6 +48,7 @@ ThemeData lightTheme = ThemeData( fixedSize: MaterialStateProperty.all(const Size(40, 25)), backgroundColor: MaterialStateProperty.all(Colors.transparent), ), + headerHeadlineStyle: const TextStyle(fontSize: 35) ), timePickerTheme: TimePickerThemeData( cancelButtonStyle: ButtonStyle( @@ -66,7 +68,8 @@ ThemeData lightTheme = ThemeData( ThemeData darkTheme = ThemeData( brightness: Brightness.dark, textTheme: TextTheme( - headlineMedium: const TextStyle(fontSize: 50, color: Colors.cyan), + headlineLarge: const TextStyle(fontSize: 50, color: Colors.cyan), + headlineMedium: const TextStyle(fontSize: 25, color: Colors.white), headlineSmall: TextStyle(fontSize: 17, color: Colors.grey[500]), labelSmall: TextStyle( fontSize: 15, color: Colors.grey[500], fontWeight: FontWeight.w400 @@ -108,7 +111,8 @@ ThemeData darkTheme = ThemeData( shape: MaterialStateProperty.all(LinearBorder.none), fixedSize: MaterialStateProperty.all(const Size(40, 25)), backgroundColor: MaterialStateProperty.all(Colors.transparent), - ) + ), + headerHeadlineStyle: const TextStyle(fontSize: 35) ), timePickerTheme: TimePickerThemeData( backgroundColor: Colors.grey[900], diff --git a/lib/widgets/generator_page/text_field.dart b/lib/widgets/generator_page/text_field.dart index b49bf9e..8fe01e7 100644 --- a/lib/widgets/generator_page/text_field.dart +++ b/lib/widgets/generator_page/text_field.dart @@ -41,6 +41,30 @@ class _CustomTextFieldState extends State { _focusNode.unfocus(); }, child: TextField( + contextMenuBuilder: (context, editableTextState) { + return AdaptiveTextSelectionToolbar( + anchors: editableTextState.contextMenuAnchors, + children: editableTextState.contextMenuButtonItems + .map((ContextMenuButtonItem buttonItem) { + return CupertinoButton( + minSize: 50, + borderRadius: const BorderRadius.all(Radius.circular(0)), + color: Colors.grey[800], + onPressed: buttonItem.onPressed, + padding: const EdgeInsets.all(10.0), + pressedOpacity: 0.7, + child: SizedBox( + width: 80, + child: Text( + CupertinoTextSelectionToolbarButton.getButtonLabel(context, buttonItem), + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.white), + ), + ), + ); + }).toList(), + ); + }, cursorColor: Theme.of(context).colorScheme.primary, controller: widget.textController, onChanged: (data) {