Commit 462cbf08 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

18-11-2025 file viewer, file paths and test cases

parent 9ff648e8
......@@ -25,12 +25,13 @@ android {
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
......@@ -65,7 +66,10 @@ android {
}
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
flutter {
source = "../.."
}
......@@ -6,8 +6,9 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission tools:node="remove" android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="My Gen"
......
package `in`.webgrid.genservices
import android.app.DownloadManager
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.webkit.CookieManager
import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
import java.util.Base64
class MainActivity : FlutterActivity() {
private val CHANNEL = "in.webgrid.genservices/download"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "startDownload") {
val url = call.argument<String>("url")
val userAgent = call.argument<String>("userAgent")
val contentDisposition = call.argument<String>("contentDisposition")
val mimeType = call.argument<String>("mimeType")
val suggestedFilename = call.argument<String>("suggestedFilename")
val isBase64 = call.argument<Boolean>("isBase64") ?: false
if (url != null && userAgent != null && mimeType != null) {
val success = startDownload(url, userAgent, contentDisposition ?: "", mimeType, suggestedFilename ?: "", isBase64)
if (success) {
result.success("Download started")
} else {
result.error("DOWNLOAD_FAILED", "Failed to start download", null)
}
} else {
result.error("INVALID_ARGUMENTS", "Missing required arguments", null)
}
} else {
result.notImplemented()
}
}
}
private fun startDownload(
url: String,
userAgent: String,
contentDisposition: String,
mimeType: String,
suggestedFilename: String,
isBase64: Boolean
): Boolean {
return try {
Toast.makeText(this, "File is being downloaded", Toast.LENGTH_SHORT).show()
// === Generate UNIQUE filename with timestamp ===
val baseName = if (suggestedFilename.isNotEmpty()) {
suggestedFilename.substringBeforeLast(".")
} else if (contentDisposition.isNotEmpty()) {
val match = Regex("filename=\"?([^\"\\s;]+)\"?").find(contentDisposition)
match?.groupValues?.get(1)?.substringBeforeLast(".") ?: "file"
} else {
url.split("/").last().substringBeforeLast(".")
}
val extension = when (mimeType.lowercase()) {
"application/pdf" -> ".pdf"
"image/jpeg", "image/jpg" -> ".jpg"
"image/png" -> ".png"
else -> ".pdf" // fallback
}
// Generate unique filename like your Dart code
val timeFormat = java.text.SimpleDateFormat("yyyyMMdd_HHmmss", java.util.Locale.getDefault())
val timestamp = timeFormat.format(java.util.Date())
val uniqueFileName = "${baseName}_$timestamp$extension"
println("Final Download File: $uniqueFileName")
if (isBase64 && url.startsWith("data:")) {
// Handle base64 (unchanged)
val base64Data = url.substringAfter("base64,")
val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), uniqueFileName)
val decodedBytes = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
java.util.Base64.getDecoder().decode(base64Data)
} else {
android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT)
}
file.writeBytes(decodedBytes)
val request = DownloadManager.Request(Uri.fromFile(file))
request.setMimeType(mimeType)
request.setTitle(uniqueFileName)
request.setDescription("Downloaded via app")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uniqueFileName)
request.setVisibleInDownloadsUi(true)
val dm = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
dm.enqueue(request)
} else {
// Regular URL download
val request = DownloadManager.Request(Uri.parse(url.trim()))
val cookies = CookieManager.getInstance().getCookie(url) ?: ""
request.addRequestHeader("Cookie", cookies)
request.addRequestHeader("User-Agent", userAgent)
request.setTitle(uniqueFileName)
request.setDescription("Downloading quotation...")
request.setMimeType(mimeType)
request.allowScanningByMediaScanner()
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// THIS IS THE KEY LINE — now with UNIQUE filename
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uniqueFileName)
request.setAllowedOverMetered(true)
request.setAllowedOverRoaming(false)
request.setVisibleInDownloadsUi(true)
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
true
} catch (e: Exception) {
e.printStackTrace()
runOnUiThread {
Toast.makeText(this, "Download failed: ${e.message}", Toast.LENGTH_LONG).show()
}
false
}
}
private fun startDownloadFile(
url: String,
userAgent: String,
contentDisposition: String,
mimeType: String,
suggestedFilename: String,
isBase64: Boolean
): Boolean {
return try {
// Show toast
Toast.makeText(this, "File is being downloaded", Toast.LENGTH_SHORT).show()
// Map MIME type to file extension
val extension = when (mimeType.lowercase()) {
"application/pdf" -> ".pdf"
"image/jpeg" -> ".jpg"
"image/png" -> ".png"
"application/msword" -> ".doc"
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" -> ".docx"
"application/vnd.ms-excel" -> ".xls"
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" -> ".xlsx"
"text/csv" -> ".csv"
else -> "" // Fallback for unknown types
}
// Determine file name
var fileName = contentDisposition.takeIf { it.isNotEmpty() }?.let {
val regex = Regex("filename=\"?([^\"\\s;]+)\"?")
regex.find(it)?.groupValues?.get(1)
} ?: suggestedFilename.takeIf { it.isNotEmpty() } ?: url.split("/").last()
// Ensure the file name has the correct extension
if (!fileName.endsWith(extension, ignoreCase = true)) {
fileName = if (fileName.contains(".")) {
fileName.substringBeforeLast(".") + extension
} else {
fileName + extension
}
}
// Sanitize file name
fileName = fileName.replace("[^a-zA-Z0-9._-]".toRegex(), "_")
// Log for debugging
println("Download File: $fileName, ContentDisposition: $contentDisposition, SuggestedFilename: $suggestedFilename, MimeType: $mimeType, IsBase64: $isBase64")
if (isBase64 && url.startsWith("data:")) {
// Handle base64 data URL
val base64Data = url.substringAfter("base64,")
val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
val decodedBytes = Base64.getDecoder().decode(base64Data)
file.writeBytes(decodedBytes)
// Optionally, use DownloadManager to notify the user
val request = DownloadManager.Request(Uri.fromFile(file))
request.setMimeType(mimeType)
request.setDescription("Downloading requested file....")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
request.setTitle(fileName)
request.setAllowedOverMetered(true)
request.setAllowedOverRoaming(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
request.setRequiresCharging(false)
request.setRequiresDeviceIdle(false)
}
request.setVisibleInDownloadsUi(true)
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
} else {
// Handle regular URL
val request = DownloadManager.Request(Uri.parse(url.trim()))
val cookies = CookieManager.getInstance().getCookie(url) ?: ""
request.addRequestHeader("Cookie", cookies)
request.addRequestHeader("User-Agent", userAgent)
request.setDescription("Downloading requested file....")
request.setMimeType(mimeType)
request.allowScanningByMediaScanner()
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
request.setTitle(fileName)
request.setAllowedOverMetered(true)
request.setAllowedOverRoaming(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
request.setRequiresCharging(false)
request.setRequiresDeviceIdle(false)
}
request.setVisibleInDownloadsUi(true)
class MainActivity : FlutterActivity()
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
......@@ -90,6 +90,7 @@ class AllServiceList {
String? inOrOutTime;
String? runningHrs;
String? fsrExt;
String? fsrFilePath;
String? fsrNo;
AllServiceList(
......@@ -105,6 +106,7 @@ class AllServiceList {
this.inOrOutTime,
this.runningHrs,
this.fsrExt,
this.fsrFilePath,
this.fsrNo});
AllServiceList.fromJson(Map<String, dynamic> json) {
......@@ -120,6 +122,7 @@ class AllServiceList {
inOrOutTime = json['in_or_out_time'];
runningHrs = json['running_hrs'];
fsrExt = json['fsr_ext'];
fsrFilePath = json['fsr_file_path'];
fsrNo = json['fsr_no'];
}
......@@ -137,6 +140,7 @@ class AllServiceList {
data['in_or_out_time'] = this.inOrOutTime;
data['running_hrs'] = this.runningHrs;
data['fsr_ext'] = this.fsrExt;
data['fsr_file_path'] = this.fsrFilePath;
data['fsr_no'] = this.fsrNo;
return data;
}
......
......@@ -152,6 +152,7 @@ class ServiceDetails {
String? inOrOutTime;
String? runningHrs;
String? fsrExt;
String? fsrFilePath;
String? fsrNo;
ServiceDetails(
......@@ -170,6 +171,7 @@ class ServiceDetails {
this.inOrOutTime,
this.runningHrs,
this.fsrExt,
this.fsrFilePath,
this.fsrNo});
ServiceDetails.fromJson(Map<String, dynamic> json) {
......@@ -188,6 +190,7 @@ class ServiceDetails {
inOrOutTime = json['in_or_out_time'];
runningHrs = json['running_hrs'];
fsrExt = json['fsr_ext'];
fsrFilePath = json['fsr_file_path'];
fsrNo = json['fsr_no'];
}
......@@ -208,6 +211,7 @@ class ServiceDetails {
data['in_or_out_time'] = this.inOrOutTime;
data['running_hrs'] = this.runningHrs;
data['fsr_ext'] = this.fsrExt;
data['fsr_file_path'] = this.fsrFilePath;
data['fsr_no'] = this.fsrNo;
return data;
}
......
......@@ -2,8 +2,10 @@ class quotationListResponse {
String? error;
List<ServiceQuotation>? serviceQuotation;
String? message;
String? sessionExists;
quotationListResponse({this.error, this.serviceQuotation, this.message});
quotationListResponse(
{this.error, this.serviceQuotation, this.message, this.sessionExists});
quotationListResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
......@@ -14,6 +16,7 @@ class quotationListResponse {
});
}
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
......@@ -24,6 +27,7 @@ class quotationListResponse {
this.serviceQuotation!.map((v) => v.toJson()).toList();
}
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
......
import 'package:flutter/foundation.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_download_manager/flutter_download_manager.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:gen_service/Models/amcQuotationListResponse.dart';
import 'package:gen_service/Models/complaintListResponse.dart';
import 'package:gen_service/Models/generatorDetailsResponse.dart';
import 'package:gen_service/Models/quotationListResponse.dart';
import 'package:gen_service/Models/scheduleListResponse.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:url_launcher/url_launcher.dart';
import '../Services/api_calling.dart';
import '../Utility/CustomSnackbar.dart';
class Generatordetailsprovider extends ChangeNotifier{
final FlutterLocalNotificationsPlugin _notificationsPlugin =
FlutterLocalNotificationsPlugin();
static const platform = MethodChannel("in.webgrid.genservices/download");
final GlobalKey webViewKey = GlobalKey();
var dl = DownloadManager();
generatorDetailsResponse? _detailsResponse;
scheduleListResponse? _scheduleResponse;
......@@ -160,4 +177,173 @@ class Generatordetailsprovider extends ChangeNotifier{
}
}
String getUniqueFilename(String baseName, [String ext = 'pdf']) {
final now = DateTime.now();
final formattedDate = DateFormat('yyyyMMdd_HHmmss').format(now);
return '${baseName}_$formattedDate.$ext';
}
void openWhatsApp(String path) async {
final Uri url = Uri.parse(path); // Example: 919876543210
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
} else {
throw 'Could not launch $url';
}
}
Future<void> handleDownload(
context,
String url,
String contentDisposition,
String mimeType,
String suggestedFilename,
) async {
// Request notification permission for Android 13+
if (Platform.isIOS) {
_handleIOSDownload(context, url, suggestedFilename);
} else if (Platform.isAndroid) {
if (await Permission.notification.request().isGranted) {
try {
// Show custom notification (optional, since DownloadManager shows its own)
if (Platform.isAndroid) {
// Call native Android Download Manager
final userAgent = 'Flutter InAppWebView';
await platform.invokeMethod('startDownload', {
'url': url,
'userAgent': userAgent,
'contentDisposition': contentDisposition,
'mimeType': mimeType,
'suggestedFilename': suggestedFilename,
});
await launchUrl(
Uri.parse(url),
mode: LaunchMode.externalApplication,
);
} else if (Platform.isIOS) {
_handleIOSDownload(context, url, suggestedFilename);
}
} catch (e) {
print("Download Error $e");
}
} else {
CustomSnackBar.showError(
context: context,
message: "Failed to Download"
);
}
}
}
Future<void> _handleIOSDownload(
context,
String url,
String suggestedFilename,
) async {
try {
// Show initial download notification
await _showDownloadNotification(0, suggestedFilename, isComplete: false);
// Get the temporary directory for iOS
final tempDir = await getTemporaryDirectory();
final fileName =
suggestedFilename.isNotEmpty
? suggestedFilename
: url.split('/').last;
final filePath = '${tempDir.path}/$fileName';
// Download the file using http
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
// Save the file
final file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
// Show completion notification
await _showDownloadNotification(100, fileName, isComplete: true);
// Optionally, open the file or notify the user
CustomSnackBar.showSuccess(
context: context,
message: "File downloaded successfully!"
);
} else {
throw Exception("Failed to download file: HTTP ${response.statusCode}");
}
} catch (e) {
print("iOS Download Error: $e");
await _showDownloadNotification(
0,
suggestedFilename,
isComplete: false,
isError: true,
);
CustomSnackBar.showError(
context: context,
message: "Failed to Download"
);
}
}
Future<void> _showDownloadNotification(
int progress,
String fileName, {
bool isComplete = false,
bool isError = false,
}) async {
final androidDetails = AndroidNotificationDetails(
'download_channel',
'Downloads',
channelDescription: 'Notifications for file downloads',
importance: Importance.high,
priority: Priority.high,
showProgress: !isComplete && !isError,
maxProgress: 100,
progress: progress,
ongoing: !isComplete && !isError,
playSound: isComplete || isError,
styleInformation: BigTextStyleInformation(
isError
? 'Download failed for $fileName'
: isComplete
? 'Download complete: $fileName'
: 'Downloading $fileName...',
),
);
final iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: isComplete || isError,
subtitle:
isError
? 'Download failed'
: isComplete
? 'Download complete'
: 'Downloading...',
threadIdentifier: 'download_thread',
);
final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
await _notificationsPlugin.show(
fileName.hashCode, // Unique ID for the notification
isError
? 'Download Failed'
: isComplete
? 'Download Complete'
: 'Downloading File',
isError
? 'Failed to download $fileName'
: isComplete
? 'Successfully downloaded $fileName'
: 'Downloading $fileName ($progress%)',
notificationDetails,
);
}
}
\ No newline at end of file
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
import 'package:http/http.dart' as http;
import 'dart:typed_data';
import 'package:photo_view/photo_view.dart';
import '../Utility/AppColors.dart';
class Fileviewer extends StatefulWidget {
final String fileName;
final String fileUrl;
const Fileviewer({super.key, required this.fileName, required this.fileUrl});
@override
State<Fileviewer> createState() => _FileviewerState();
}
class _FileviewerState extends State<Fileviewer> {
final Completer<InAppWebViewController> _controller =
Completer<InAppWebViewController>();
var empId = "";
var sessionId = "";
bool isLoading = true;
InAppWebViewController? webViewController;
PullToRefreshController? pullToRefreshController;
PullToRefreshSettings pullToRefreshSettings = PullToRefreshSettings(
color: AppColors.nearDarkText,
);
bool pullToRefreshEnabled = true;
final GlobalKey webViewKey = GlobalKey();
// Zoom control variables
PhotoViewController _photoViewController = PhotoViewController();
PhotoViewScaleStateController _scaleStateController = PhotoViewScaleStateController();
String getFileExtension(String fileName) {
print(widget.fileUrl);
print(fileName);
return fileName.split('.').last.toLowerCase();
}
Future<void> _launchUrl(String url) async {
final Uri uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
throw 'Could not launch $url';
}
}
var Finalurl;
@override
void initState() {
pullToRefreshController =
kIsWeb
? null
: PullToRefreshController(
settings: pullToRefreshSettings,
onRefresh: () async {
if (defaultTargetPlatform == TargetPlatform.android) {
webViewController?.reload();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
webViewController?.loadUrl(
urlRequest: URLRequest(
url: await webViewController?.getUrl(),
),
);
}
},
);
// Initialize photo view controllers
_photoViewController = PhotoViewController();
_scaleStateController = PhotoViewScaleStateController();
super.initState();
}
@override
void dispose() {
_photoViewController.dispose();
_scaleStateController.dispose();
pullToRefreshController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 8),
InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.pop(context, true);
},
child: SvgPicture.asset(
"assets/svg/appbar_back.svg",
color: AppColors.nearDarkText,
height: 25,
),
),
SizedBox(width: 10),
Expanded(
flex: 4,
child: InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.pop(context, true);
},
child: Text(
"File Viewer",
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 16,
color: AppColors.nearDarkText,
height: 1.1,
),
),
),
),
],
),
),
body: SafeArea(
child: Center(
child: fileWidget(context)
),
),
);
}
Widget fileWidget(BuildContext context) {
final extension = getFileExtension(widget.fileName);
switch (extension) {
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
case 'bmp':
case 'webp':
return _buildImageViewer();
case 'pdf':
return _buildPdfViewer();
case 'doc':
case 'docx':
case 'xls':
case 'xlsx':
case 'ppt':
case 'pptx':
return _buildDocumentViewer();
default:
return _buildUnsupportedViewer();
}
}
Widget _buildImageViewer() {
return PhotoView(
imageProvider: CachedNetworkImageProvider(
widget.fileUrl),
loadingBuilder: (context, event) => Center(
child: SizedBox(
width: 40,
height: 40,
child: CircularProgressIndicator(
value: event == null ? 0 : event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 1),
),
),
),
errorBuilder: (context, error, stackTrace) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, color: Colors.red, size: 50),
SizedBox(height: 10),
Text(
'Failed to load image',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
),
backgroundDecoration: BoxDecoration(color: Colors.white),
minScale: PhotoViewComputedScale.contained * 0.5,
maxScale: PhotoViewComputedScale.covered * 4.0,
initialScale: PhotoViewComputedScale.contained,
basePosition: Alignment.center,
scaleStateController: _scaleStateController,
controller: _photoViewController,
enableRotation: true,
gestureDetectorBehavior: HitTestBehavior.deferToChild,
filterQuality: FilterQuality.high,
);
}
Widget _buildPdfViewer() {
return SfPdfViewer.network(
widget.fileUrl,
key: GlobalKey(),
canShowScrollHead: true,
canShowPaginationDialog: true,
pageLayoutMode: PdfPageLayoutMode.single,
interactionMode: PdfInteractionMode.pan,
enableDoubleTapZooming: true,
enableTextSelection: true,
onZoomLevelChanged: (PdfZoomDetails details) {
// Use the correct property name
//print('Zoom level changed: ${details.zoomLevel}');
},
);
}
Widget _buildDocumentViewer() {
return Stack(
children: [
InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(url: WebUri(widget.fileUrl)),
androidOnGeolocationPermissionsShowPrompt: (
InAppWebViewController controller,
String origin,
) async {
return GeolocationPermissionShowPromptResponse(
origin: origin,
allow: true,
retain: true,
);
},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
javaScriptEnabled: true,
clearCache: true,
supportZoom: true,
),
android: AndroidInAppWebViewOptions(
useWideViewPort: true,
loadWithOverviewMode: true,
allowContentAccess: true,
geolocationEnabled: true,
allowFileAccess: true,
databaseEnabled: true,
domStorageEnabled: true,
builtInZoomControls: true,
displayZoomControls: false,
safeBrowsingEnabled: true,
clearSessionCache: true,
supportMultipleWindows: false,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
allowsAirPlayForMediaPlayback: true,
allowsPictureInPictureMediaPlayback: true,
allowsBackForwardNavigationGestures: true,
allowsLinkPreview: true,
isFraudulentWebsiteWarningEnabled: true,
),
),
androidOnPermissionRequest: (
InAppWebViewController controller,
String origin,
List<String> resources,
) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT,
);
},
onWebViewCreated: (controller) {
webViewController = controller;
_controller.complete(controller);
},
pullToRefreshController: pullToRefreshController,
onLoadStart: (controller, url) {
setState(() {
isLoading = true;
});
},
onLoadStop: (controller, url) {
pullToRefreshController?.endRefreshing();
setState(() {
isLoading = false;
});
// Enable zooming in WebView
controller.evaluateJavascript(source: """
var meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=4.0, user-scalable=yes';
document.getElementsByTagName('head')[0].appendChild(meta);
""");
},
onReceivedError: (controller, request, error) {
pullToRefreshController?.endRefreshing();
setState(() {
isLoading = false;
});
},
onProgressChanged: (controller, progress) {
if (progress == 100) {
pullToRefreshController?.endRefreshing();
}
},
onConsoleMessage: (controller, consoleMessage) {
if (kDebugMode) {
debugPrint("consoleMessage: ${consoleMessage.message}");
}
},
),
// Loading indicator for documents
if (isLoading)
Positioned.fill(
child: Container(
color: Colors.black.withOpacity(0.3),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(AppColors.nearDarkText),
),
SizedBox(height: 10),
Text(
'Loading Document...',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
],
),
),
),
),
),
],
);
}
Widget _buildUnsupportedViewer() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.insert_drive_file,
size: 64,
color: Colors.grey[400],
),
SizedBox(height: 16),
Text(
'Unsupported File Format',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.grey[600],
),
),
SizedBox(height: 8),
Text(
'Format: ${getFileExtension(widget.fileName).toUpperCase()}',
style: TextStyle(
fontSize: 14,
color: Colors.grey[500],
),
),
SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () {
_launchUrl(widget.fileUrl);
},
icon: Icon(Icons.open_in_new),
label: Text('Open in External App'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.nearDarkText,
foregroundColor: Colors.white,
),
),
],
),
);
}
Future<Uint8List?> _loadPdf(String url) async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return response.bodyBytes;
}
} catch (e) {
print('Error loading PDF: $e');
}
return null;
}
}
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Screens/AuthScreen/LoginScreen.dart';
import 'package:gen_service/Utility/CustomSnackbar.dart';
......@@ -444,6 +445,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
},
child: Scaffold(
backgroundColor: Color(0xFF4076FF),
body: CustomScrollView(
physics: ClampingScrollPhysics(),
slivers: <Widget>[
......@@ -457,8 +459,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
Provider.of<DashboardProvider>(context, listen: false);
profileProvider.fetchProfile(widget.accId, widget.sessionId);
},
stretchTriggerOffset: 300.0,
expandedHeight: 260.0,
stretchTriggerOffset: 340.0,
expandedHeight: 300.0,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final top = constraints.biggest.height;
......@@ -467,102 +469,134 @@ class _ProfileScreenState extends State<ProfileScreen> {
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: GestureDetector(
child: Container(
width: double.infinity,
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (data.profileImg?.isNotEmpty == true)
? Image.network(
data.profileImg.toString(),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
)
: CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
background: Container(
width: double.infinity,
decoration: const BoxDecoration(gradient: AppColors.backgroundGradient),
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.pop(context, true);
},
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
"assets/svg/appbar_back.svg",
height: 25,
),
),
),
const SizedBox(height: 16),
Flexible(
child: Text(
data.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w400,
SizedBox(width: 10),
Expanded(
flex: 4,
child: InkResponse(
onTap: () {
HapticFeedback.selectionClick();
Navigator.pop(context, true);
},
child: Text(
"Profile",
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 16,
color: Colors.white,
height: 1.1,
),
),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
// Profile Image
Container(
height: 80,
width: 80,
decoration: const BoxDecoration(
color: Color(0xFFE6F6FF),
shape: BoxShape.circle,
),
const SizedBox(height: 8),
Text(
'+91 ${data.mobNum}',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white.withOpacity(0.9),
fontSize: 16,
clipBehavior: Clip.antiAlias,
child: (data.profileImg?.isNotEmpty == true)
? Image.network(
data.profileImg.toString(),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
),
)
: CircleAvatar(
radius: 40,
backgroundColor: const Color(0xFFE0F4FF),
child: SvgPicture.asset(
height: 40,
"assets/svg/person_ic.svg",
fit: BoxFit.contain,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 16),
Text(
data.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Text(
'+91 ${data.mobNum}',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white.withOpacity(0.9),
fontSize: 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 12,),
InkResponse(
onTap: () => onLogout(context),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
" Logout ",
style: TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w600,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 12,),
InkResponse(
onTap: () => onLogout(context),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
" Logout ",
style: TextStyle(
color: AppColors.normalText,
fontWeight: FontWeight.w600,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
),
),
SizedBox(height: 2,),
],
),
SizedBox(height: 2,),
],
),
),
),
......@@ -573,7 +607,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
),
// Body content
SliverToBoxAdapter(
SliverFillRemaining(
hasScrollBody: false,
child: Container(
padding: const EdgeInsets.only(top: 1, bottom: 0),
color: Colors.transparent,
......@@ -600,7 +635,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
title: "Email ID",
subTitle: data.email.toString()
),
const SizedBox(height: 8),
// address
_buildItemRow(
icon: "assets/svg/lolipop_ic.svg",
......@@ -608,8 +642,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
title: "Address",
subTitle: data.address.toString()
),
const SizedBox(height: 4),
// state
_buildItemRow(
icon: "assets/svg/pay_card_ic.svg",
......@@ -617,7 +649,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
title: "State",
subTitle: data.state.toString()
),
const SizedBox(height: 4),
// sub local
_buildItemRow(
icon: "assets/svg/pay_card_ic.svg",
......@@ -625,39 +656,37 @@ class _ProfileScreenState extends State<ProfileScreen> {
title: "Sub Locality",
subTitle: data.locality.toString()
),
const SizedBox(height: 120),
Padding(
padding: const EdgeInsets.only( bottom: 10, left: 20, right: 20),
child: Center(
child: Column(
children: [
SvgPicture.asset(
"assets/svg/genesis_logo_2io.svg",
height: 55,
color: AppColors.buttonColor,
),
SizedBox(height: 12,),
Text(
'Genesis Poweronics PVT. Ltd.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
Text(
'App Version 1.0',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
],
),
Spacer(),
Align(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SvgPicture.asset(
"assets/svg/genesis_logo_2io.svg",
height: 55,
color: AppColors.buttonColor,
),
SizedBox(height: 12,),
Text(
'Genesis Poweronics PVT. Ltd.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
Text(
'App Version 1.0',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.subtitleText),
),
],
),
),
const SizedBox(height: 80),
],
),
),
......@@ -683,7 +712,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
}) {
final isShow = title != "Sub Locality" && title != "State";
return Container(
padding: EdgeInsets.all(16),
padding: EdgeInsets.symmetric(horizontal: 16,vertical: 8),
child: Row(
children: [
......
......@@ -4,10 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gen_service/Notifiers/generatorDetailsProvider.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/ComplaintDetailsScreen.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/ComplaintListScreen.dart';
import 'package:gen_service/Screens/HelpAndComplaintScreens/SelectOrderHelpScreen.dart';
import 'package:gen_service/Screens/amcQuotationListScreen.dart';
import 'package:gen_service/Screens/complaintListScreen.dart';
import 'package:gen_service/Screens/quotationListScreen.dart';
import 'package:gen_service/Screens/scheduleListScreen.dart';
import 'package:provider/provider.dart';
......@@ -698,6 +698,8 @@ class _GeneratordetailsscreenState extends State<Generatordetailsscreen> {
(i) => _buildListItem1(
quotationsList[i]!.title ?? "-",
quotationsList[i]!.date ?? "-",
detailsProvider,
quotationsList[i]!.filePath
),
),
],
......@@ -1076,18 +1078,62 @@ class _GeneratordetailsscreenState extends State<Generatordetailsscreen> {
);
}
Widget _buildListItem1(String left, String right) {
return Container(
height: 50,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text(left), Text(right)],
Widget _buildListItem1(String left, String right,Generatordetailsprovider provider,filepath) {
return InkResponse(
onTap: () {
// _handleDownload(
// context,
// data.quoteFilepath!,
// contentDisposition,
// 'application/octet-stream',
// '',
// );
String suggestedFilename = provider
.getUniqueFilename('quotation', 'pdf');
String contentDisposition =
'attachment; filename="$suggestedFilename"';
provider.handleDownload(
context,
filepath,
contentDisposition,
'application/octet-stream',
'',
);
},
child: Container(
height: 50,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 4,
child: Text(
left,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: TextStyle(fontSize: 14),
),
),
Expanded(
flex: 3,
child: Text(
right,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right,
style: TextStyle(fontSize: 14),
),
),
],
),
),
);
}
......@@ -1100,98 +1146,116 @@ class _GeneratordetailsscreenState extends State<Generatordetailsscreen> {
color: Colors.white,
borderRadius: BorderRadius.circular(14),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 7,
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: const TextStyle(
fontFamily: 'Poppins',
color: Color(0xFF008CDE),
fontSize: 14,
child: InkResponse(
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => ComplaintDetailsScreen(
accId: widget.accId,
sessionId: widget.sessionId,
complaintId: data.id,
),
),
);
},
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 7,
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: const TextStyle(
fontFamily: 'Poppins',
color: Color(0xFF008CDE),
fontSize: 14,
),
children: [
TextSpan(text: "#${data.id}"),
TextSpan(text: " | ${data.complaintName}"),
],
),
children: [
TextSpan(text: "#${data.id}"),
TextSpan(text: " | ${data.complaintName}"),
],
),
),
Text(
"${data.registredDate}",
style: TextStyle(
color: AppColors.subtitleText,
fontSize: 12,
Text(
"${data.registredDate}",
style: TextStyle(
color: AppColors.subtitleText,
fontSize: 12,
),
),
),
],
],
),
),
),
),
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppColors.successBG,
),
child: Center(
child: Text(
"${data.openStatus}",
style: TextStyle(fontSize: 14, color: AppColors.success),
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppColors.successBG,
),
child: Center(
child: Text(
"${data.openStatus}",
style: TextStyle(
fontSize: 14,
color: AppColors.success,
),
),
),
),
),
),
],
),
Divider(color: Color(0xFF777777), thickness: 0.3),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 5,
child: Text(
"${data.productName}",
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: const TextStyle(
color: AppColors.nearDarkText,
fontSize: 12,
],
),
Divider(color: Color(0xFF777777), thickness: 0.3),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 5,
child: Text(
"${data.productName}",
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: const TextStyle(
color: AppColors.nearDarkText,
fontSize: 12,
),
),
),
),
Expanded(
flex: 5,
child: RichText(
maxLines: 1,
textAlign: TextAlign.right,
text: TextSpan(
style: TextStyle(
fontFamily: 'Poppins',
color: AppColors.subtitleText,
fontSize: 12,
Expanded(
flex: 5,
child: RichText(
maxLines: 1,
textAlign: TextAlign.right,
text: TextSpan(
style: TextStyle(
fontFamily: 'Poppins',
color: AppColors.subtitleText,
fontSize: 12,
),
children: [
TextSpan(text: "#${data.hashId}"),
TextSpan(text: " | Engine: ${data.modelName}"),
],
),
children: [
TextSpan(text: "#${data.hashId}"),
TextSpan(text: " | Engine: ${data.modelName}"),
],
),
),
),
],
),
],
],
),
],
),
),
);
}
......
......@@ -121,7 +121,11 @@ class _QuotationListScreenState extends State<QuotationListScreen> {
context,
listen: false,
);
await provider.fetchQuotationList(widget.accId, widget.sessionId, widget.genId);
await provider.fetchQuotationList(
widget.accId,
widget.sessionId,
widget.genId,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonColor,
......@@ -277,41 +281,64 @@ class _QuotationListScreenState extends State<QuotationListScreen> {
physics: const NeverScrollableScrollPhysics(),
itemCount: data!.length,
itemBuilder: (context, j) {
return Container(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
margin: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: Text(
data[j].title ?? "-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
return InkResponse(
onTap: () {
// _handleDownload(
// context,
// data.quoteFilepath!,
// contentDisposition,
// 'application/octet-stream',
// '',
// );
String suggestedFilename = provider
.getUniqueFilename('quotation', 'pdf');
String contentDisposition =
'attachment; filename="$suggestedFilename"';
provider.handleDownload(
context,
data[j].fileLoc!,
contentDisposition,
'application/octet-stream',
'',
);
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
margin: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 4,
child: Text(
data[j].title ?? "-",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
),
),
),
Spacer(),
Expanded(
flex: 1,
child: Text(
data[j].date ?? "-",
textAlign: TextAlign.right,
style: TextStyle(),
Expanded(
flex: 3,
child: Text(
data[j].date ?? "-",
textAlign: TextAlign.right,
style: TextStyle(fontSize: 14),
),
),
),
],
],
),
),
);
},
......
......@@ -7,20 +7,28 @@ import Foundation
import connectivity_plus
import device_info_plus
import flutter_inappwebview_macos
import flutter_local_notifications
import geolocator_apple
import location
import package_info_plus
import path_provider_foundation
import shared_preferences_foundation
import sqflite_darwin
import syncfusion_pdfviewer_macos
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
......@@ -33,6 +33,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
characters:
dependency: transitive
description:
......@@ -73,6 +97,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
crypto:
dependency: transitive
description:
......@@ -109,10 +141,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: dd0e8e02186b2196c7848c9d394a5fd6e5b57a43a546082c5820b1ec72317e33
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
url: "https://pub.dev"
source: hosted
version: "12.2.0"
version: "11.5.0"
device_info_plus_platform_interface:
dependency: transitive
description:
......@@ -121,6 +153,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.3"
dio:
dependency: transitive
description:
name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.dev"
source: hosted
version: "5.9.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
dropdown_button2:
dependency: "direct main"
description:
......@@ -174,6 +222,86 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_download_manager:
dependency: "direct main"
description:
name: flutter_download_manager
sha256: f0e604be623f2559a2dc2ccb0918e4e5224ff28a02b864ba0c27a3c6aca6c154
url: "https://pub.dev"
source: hosted
version: "0.5.5"
flutter_inappwebview:
dependency: "direct main"
description:
name: flutter_inappwebview
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
flutter_inappwebview_android:
dependency: transitive
description:
name: flutter_inappwebview_android
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
flutter_inappwebview_internal_annotations:
dependency: transitive
description:
name: flutter_inappwebview_internal_annotations
sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
flutter_inappwebview_ios:
dependency: transitive
description:
name: flutter_inappwebview_ios
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_macos:
dependency: transitive
description:
name: flutter_inappwebview_macos
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_platform_interface:
dependency: transitive
description:
name: flutter_inappwebview_platform_interface
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev"
source: hosted
version: "1.3.0+1"
flutter_inappwebview_web:
dependency: transitive
description:
name: flutter_inappwebview_web
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_windows:
dependency: transitive
description:
name: flutter_inappwebview_windows
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints:
dependency: "direct dev"
description:
......@@ -182,6 +310,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875"
url: "https://pub.dev"
source: hosted
version: "19.5.0"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
flutter_local_notifications_windows:
dependency: transitive
description:
name: flutter_local_notifications_windows
sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
flutter_pdfview:
dependency: "direct main"
description:
name: flutter_pdfview
sha256: c0b2cc4ebf461a5a4bb9312a165222475a7d93845c7a0703f4abb7f442eb6d54
url: "https://pub.dev"
source: hosted
version: "1.4.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
......@@ -400,6 +568,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
......@@ -480,6 +656,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
......@@ -496,6 +680,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.0"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
open_filex:
dependency: "direct main"
description:
......@@ -688,6 +880,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
rxdart:
dependency: transitive
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
sanitize_html:
dependency: transitive
description:
......@@ -765,6 +965,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
url: "https://pub.dev"
source: hosted
version: "2.5.5"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
stack_trace:
dependency: transitive
description:
......@@ -797,6 +1037,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: bee87cdfe527b31705190162e3bd27bf468d591ae7c68182244bdfd94e21f737
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_flutter_pdf:
dependency: transitive
description:
name: syncfusion_flutter_pdf
sha256: "08fc998934dab953e980ab3b98482e994beda2e736b88c9fec99cdcf04036509"
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_flutter_pdfviewer:
dependency: "direct main"
description:
name: syncfusion_flutter_pdfviewer
sha256: "240c3c50e5a9a0ed2ca9d0449414f83ddcb09dafc798362bf8d8db11dabffdfe"
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_flutter_signaturepad:
dependency: transitive
description:
name: syncfusion_flutter_signaturepad
sha256: "903dcd8a6afcd94488be296f9e45c28e2ea6634849eccb70066e310db4198edc"
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_pdfviewer_macos:
dependency: transitive
description:
name: syncfusion_pdfviewer_macos
sha256: "529befb03610bc5f647ddba84150355bea18a25f5ce8329f3065971a498b89d1"
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_pdfviewer_platform_interface:
dependency: transitive
description:
name: syncfusion_pdfviewer_platform_interface
sha256: e27a16b987b7b0d241a153ac96119c2bf753075cae93301e0143ebf7c139503b
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_pdfviewer_web:
dependency: transitive
description:
name: syncfusion_pdfviewer_web
sha256: "1cf1d8f8871fd421e454e7e38f1044b6b170bb0201a5bee8634210d27aa79e2e"
url: "https://pub.dev"
source: hosted
version: "29.2.11"
syncfusion_pdfviewer_windows:
dependency: transitive
description:
name: syncfusion_pdfviewer_windows
sha256: cc6826cdae73657d010bd4480b5c6f06a78589e86185eeec9f6ec931c4c4276a
url: "https://pub.dev"
source: hosted
version: "29.2.11"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
term_glyph:
dependency: transitive
description:
......@@ -813,6 +1125,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.4"
timezone:
dependency: transitive
description:
name: timezone
sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
url: "https://pub.dev"
source: hosted
version: "0.10.1"
typed_data:
dependency: transitive
description:
......
......@@ -43,7 +43,7 @@ dependencies:
pull_to_refresh: ^2.0.0
photo_view: ^0.15.0
android_id: ^0.4.0
device_info_plus: ^12.2.0
device_info_plus: ^11.4.0
dropdown_button2: ^2.3.9
url_launcher: ^6.3.2
path_provider: ^2.1.5
......@@ -54,6 +54,13 @@ dependencies:
permission_handler: ^12.0.1
geocoding: ^4.0.0
razorpay_flutter: ^1.4.0
flutter_inappwebview: ^6.1.5
flutter_pdfview: ^1.4.3
cached_network_image: ^3.4.1
syncfusion_flutter_pdfviewer: ^29.2.4
flutter_local_notifications: ^19.5.0
flutter_download_manager: ^0.5.5
intl: ^0.20.2
dev_dependencies:
flutter_test:
......
......@@ -7,17 +7,23 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <syncfusion_pdfviewer_windows/syncfusion_pdfviewer_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}
......@@ -4,12 +4,15 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
flutter_inappwebview_windows
geolocator_windows
permission_handler_windows
syncfusion_pdfviewer_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_local_notifications_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment