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_inappwebview/flutter_inappwebview.dart'; import 'package:generp/Utils/commonWidgets.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 '../../Utils/app_colors.dart'; class Fileviewer extends StatefulWidget { final String fileName; final String fileUrl; const Fileviewer({super.key, required this.fileName, required this.fileUrl}); @override State createState() => _FileviewerState(); } class _FileviewerState extends State { final Completer _controller = Completer(); var empId = ""; var sessionId = ""; bool isLoading = true; InAppWebViewController? webViewController; PullToRefreshController? pullToRefreshController; PullToRefreshSettings pullToRefreshSettings = PullToRefreshSettings( color: AppColors.app_blue, ); bool pullToRefreshEnabled = true; final GlobalKey webViewKey = GlobalKey(); // Zoom control variables PhotoViewController _photoViewController = PhotoViewController(); PhotoViewScaleStateController _scaleStateController = PhotoViewScaleStateController(); String getFileExtension(String fileName) { print(widget.fileUrl); return fileName.split('.').last.toLowerCase(); } Future _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: appbarNew(context, "File Viewer", 0xFFFFFFFF), 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: Container( 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 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(AppColors.app_blue), ), 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.app_blue, foregroundColor: Colors.white, ), ), ], ), ); } Future _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; } }