diff --git a/lib/core/utils/string_extensions.dart b/lib/core/utils/string_extensions.dart index 3a8c756..060d8df 100644 --- a/lib/core/utils/string_extensions.dart +++ b/lib/core/utils/string_extensions.dart @@ -31,7 +31,7 @@ extension MyStringExtensions on String? { String fileNameWithoutExtension() { if (this == null || this!.trim().isEmpty) return ''; - + this!.replaceAll(RegExp(r'[^a-zA-Z0-9\.\-]'), '_'); final parts = this!.split('.'); if (parts.length < 2) return this!; // Nessuna estensione trovata diff --git a/lib/core/widgets/image_viewer_widget.dart b/lib/core/widgets/image_viewer_widget.dart new file mode 100644 index 0000000..f0cf481 --- /dev/null +++ b/lib/core/widgets/image_viewer_widget.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ImageViewerWidget extends StatelessWidget { + final String url; + + const ImageViewerWidget({super.key, required this.url}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.pop(context), + ), + ), + body: InteractiveViewer( + // InteractiveViewer dà lo zoom gratis alle immagini! + child: Center(child: Image.network(url)), + ), + ); + } +} diff --git a/lib/core/widgets/pdf_viewer_widget.dart b/lib/core/widgets/pdf_viewer_widget.dart new file mode 100644 index 0000000..f7a269b --- /dev/null +++ b/lib/core/widgets/pdf_viewer_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:pdfx/pdfx.dart'; +import 'package:internet_file/internet_file.dart'; // flutter pub add internet_file + +class PdfViewerWidget extends StatefulWidget { + final String url; + + const PdfViewerWidget({super.key, required this.url}); + + @override + State createState() => _PdfViewerWidgetState(); +} + +class _PdfViewerWidgetState extends State { + late PdfControllerPinch _pdfController; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _initPdf(); + } + + Future _initPdf() async { + // Scarica il file in memoria in modo fluido + final pdfData = await InternetFile.get(widget.url); + _pdfController = PdfControllerPinch( + document: PdfDocument.openData(pdfData), + ); + if (mounted) setState(() => _isLoading = false); + } + + @override + void dispose() { + _pdfController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (_isLoading) { + return const Center(child: CircularProgressIndicator()); + } + return Scaffold( + // Usiamo Scaffold dentro il Dialog per avere l'AppBar e poter chiudere + appBar: AppBar( + title: const Text("Visualizzatore PDF"), + leading: IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.pop(context), + ), + ), + body: PdfViewPinch( + controller: _pdfController, + // pdfx gestisce nativamente il pinch to zoom! + ), + ); + } +} diff --git a/lib/features/customers/data/customer_repository.dart b/lib/features/customers/data/customer_repository.dart index 06302fe..bc84a17 100644 --- a/lib/features/customers/data/customer_repository.dart +++ b/lib/features/customers/data/customer_repository.dart @@ -90,7 +90,7 @@ class CustomerRepository { } /// Salva il riferimento del file nel DB - Future saveFileReference(CustomerFileModel file) async { + Future saveCustomerFile(CustomerFileModel file) async { await _supabase.from('customer_file').insert(file.toMap()); } @@ -149,6 +149,10 @@ class CustomerRepository { } } + Future saveFileReference(CustomerFileModel file) async { + await _supabase.from('customer_file').upsert(file.toMap()); + } + /// Aggiorna la lista degli URL nel database Future updateCustomerDocuments(int id, List urls) async { await _supabase diff --git a/lib/features/services/blocs/services_cubit.dart b/lib/features/services/blocs/services_cubit.dart index 0a6a241..e4b7640 100644 --- a/lib/features/services/blocs/services_cubit.dart +++ b/lib/features/services/blocs/services_cubit.dart @@ -3,10 +3,12 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/core/blocs/session/session_bloc.dart'; +import 'package:flux/core/utils/string_extensions.dart'; import 'package:flux/features/services/data/services_repository.dart'; import 'package:flux/features/services/models/energy_service_model.dart'; import 'package:flux/features/services/models/entertainment_service_model.dart'; import 'package:flux/features/services/models/fin_service_model.dart'; +import 'package:flux/features/services/models/service_file_model.dart'; import 'package:flux/features/services/models/service_model.dart'; import 'package:get_it/get_it.dart'; import 'package:collection/collection.dart'; @@ -130,7 +132,7 @@ class ServicesCubit extends Cubit { companyId: _sessionBloc.state.company!.id, ), status: ServicesStatus.ready, - localAttachments: [], + files: [], ), ); } @@ -210,7 +212,7 @@ class ServicesCubit extends Cubit { final serviceToSave = state.currentService!.copyWith(isBozza: isBozza); // 2. Salvataggio corazzato - await _repository.saveFullService(serviceToSave, state.localAttachments); + await _repository.saveFullService(serviceToSave, state.files); // 3. Reset e ricaricamento emit(state.copyWith(status: ServicesStatus.saved, currentService: null)); @@ -228,14 +230,90 @@ class ServicesCubit extends Cubit { // --- GESTIONE ALLEGATI LOCALI --- void addAttachments(List files) { - // Aggiungiamo i nuovi file a quelli già presenti in memoria - final updatedList = [...state.localAttachments, ...files]; - emit(state.copyWith(localAttachments: updatedList)); + // Trasformiamo i PlatformFile in ServiceFileModel "temporanei" + final newAttachments = files.map((file) { + return ServiceFileModel( + id: '', // ID vuoto perché non ancora su DB + serviceId: state.currentService?.id ?? '', + name: file.name.fileNameWithoutExtension(), + extension: file.name.fileExtension(), + url: '', // URL vuoto perché non ancora caricato + fileSize: file.size, + localBytes: file.bytes, // Fondamentale per l'upload! + createdAt: DateTime.now(), + ); + }).toList(); + + // Uniamo i file esistenti (remoti + locali già aggiunti) con i nuovi + final updatedList = [ + ...(state.currentService?.files ?? []), + ...newAttachments, + ]; + + emit( + state.copyWith( + currentService: state.currentService?.copyWith(files: updatedList), + ), + ); } - void removeLocalAttachment(int index) { - final updatedList = List.from(state.localAttachments); + void removeAttachment(int index) { + if (state.currentService == null) return; + + final updatedList = List.from( + state.currentService!.files, + ); updatedList.removeAt(index); - emit(state.copyWith(localAttachments: updatedList)); + + emit( + state.copyWith( + currentService: state.currentService?.copyWith(files: updatedList), + ), + ); + } + + void saveAndCopyFileToCustomer(ServiceFileModel file) async { + final currentService = state.currentService; + if (currentService == null || currentService.customerId == null) { + // Magari mostra un errore: non posso copiare al cliente se non c'è un cliente! + return; + } + + emit(state.copyWith(status: ServicesStatus.loading)); + + try { + // 1. Salviamo la pratica (Bozza o definitiva che sia) + // Questo assicura che il file sia stato caricato su Storage e censito su DB + await saveCurrentService(isBozza: currentService.isBozza); + + // 2. Recuperiamo il file "aggiornato" + // Dopo il saveCurrentService, il file che prima era "locale" ora ha un URL. + // Lo cerchiamo nella lista aggiornata per nome o estensione. + final savedFile = state.currentService!.files.firstWhere( + (f) => f.name == file.name && f.extension == file.extension, + orElse: () => file, + ); + + if (savedFile.url.isEmpty) { + throw Exception("Errore: URL del file non trovato dopo il salvataggio."); + } + + // 3. Chiamiamo il repository per la copia fisica nel database del cliente + // Passiamo l'URL del file e l'ID del cliente + await _repository.copyFileToCustomer( + file: savedFile, + customerId: currentService.customerId!, + ); + + // 4. Feedback all'utente + // Potresti emettere un successo o mostrare un toast + emit(state.copyWith(status: ServicesStatus.success)); + + } catch (e) { + emit(state.copyWith( + status: ServicesStatus.failure, + errorMessage: "Errore durante la copia del file: $e", + )); + } } } diff --git a/lib/features/services/blocs/services_state.dart b/lib/features/services/blocs/services_state.dart index 4881ecd..f04c002 100644 --- a/lib/features/services/blocs/services_state.dart +++ b/lib/features/services/blocs/services_state.dart @@ -10,7 +10,7 @@ class ServicesState extends Equatable { final String query; final DateTimeRange? dateRange; final bool hasReachedMax; - final List localAttachments; + final List files; const ServicesState({ required this.status, @@ -20,7 +20,7 @@ class ServicesState extends Equatable { this.query = '', this.dateRange, this.hasReachedMax = false, - this.localAttachments = const [], + this.files = const [], }); ServicesState copyWith({ @@ -31,7 +31,7 @@ class ServicesState extends Equatable { String? query, DateTimeRange? dateRange, bool? hasReachedMax, - List? localAttachments, + List? files, }) { return ServicesState( status: status ?? this.status, @@ -41,7 +41,7 @@ class ServicesState extends Equatable { query: query ?? this.query, dateRange: dateRange ?? this.dateRange, hasReachedMax: hasReachedMax ?? this.hasReachedMax, - localAttachments: localAttachments ?? this.localAttachments, + files: files ?? this.files, ); } @@ -54,6 +54,6 @@ class ServicesState extends Equatable { query, dateRange, hasReachedMax, - localAttachments, + files, ]; } diff --git a/lib/features/services/data/services_repository.dart b/lib/features/services/data/services_repository.dart index 899b8da..52ca7a7 100644 --- a/lib/features/services/data/services_repository.dart +++ b/lib/features/services/data/services_repository.dart @@ -1,7 +1,7 @@ -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flux/core/blocs/session/session_bloc.dart'; -import 'package:flux/core/utils/string_extensions.dart'; +import 'package:flux/features/customers/data/customer_repository.dart'; +import 'package:flux/features/customers/models/customer_file_model.dart'; import 'package:flux/features/services/models/service_file_model.dart'; import 'package:get_it/get_it.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; @@ -10,6 +10,7 @@ import '../models/service_model.dart'; class ServicesRepository { final _supabase = Supabase.instance.client; final companyId = GetIt.I.get().state.company!.id; + final CustomerRepository _customerRepository = GetIt.I(); // --- RECUPERO SINGOLO SERVIZIO CON JOIN COMPLETO --- Future fetchServiceById(String id) async { @@ -84,7 +85,7 @@ class ServicesRepository { // --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) --- Future saveFullService( ServiceModel service, - List localFiles, + List files, ) async { try { // 1. Upsert del record principale @@ -152,40 +153,27 @@ class ServicesRepository { if (insertTasks.isNotEmpty) { await Future.wait(insertTasks); } - if (localFiles.isNotEmpty) { + if (files.isNotEmpty) { final List uploadTasks = []; - for (var file in localFiles) { - // Puliamo il nome del file per evitare problemi con spazi o caratteri strani - final cleanFileName = file.name.replaceAll( - RegExp(r'[^a-zA-Z0-9\.\-]'), - '_', - ); + for (var file in files) { final storagePath = - '$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName'; - - final int fileSize = file.size; - - final fileToSave = ServiceFileModel( - name: cleanFileName.fileNameWithoutExtension(), - extension: cleanFileName.fileExtension(), - url: '', - serviceId: newId, - fileSize: fileSize, - ); + '$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_${file.name}.${file.extension}'; + final String mimeType = file.extension.toLowerCase() == 'pdf' + ? 'application/pdf' + : 'image/${file.extension}'; + final fileToSave = file.copyWith(serviceId: newId); // Creiamo una funzione asincrona per caricare file e scrivere nel DB Future uploadAndLink() async { // Determiniamo il MIME type corretto in base all'estensione - final String mimeType = fileToSave.extension.toLowerCase() == 'pdf' - ? 'application/pdf' - : 'image/${fileToSave.extension}'; + // A. Upload nel Bucket Storage (usiamo i bytes così funziona anche su Web!) await _supabase.storage .from('documents') .uploadBinary( storagePath, - file.bytes!, + fileToSave.localBytes!, fileOptions: FileOptions( contentType: mimeType, // Diciamo a Supabase esattamente cos'è! @@ -255,4 +243,18 @@ class ServicesRepository { ]; // Fallback se non c'è ancora storia } } + + Future copyFileToCustomer({ + required ServiceFileModel file, + required String customerId, + }) async { + CustomerFileModel fileToCopy = CustomerFileModel( + customerId: customerId, + name: file.name, + url: file.url, + extension: file.extension, + fileSize: file.fileSize, + ); + await _customerRepository.saveCustomerFile(fileToCopy); + } } diff --git a/lib/features/services/models/service_file_model.dart b/lib/features/services/models/service_file_model.dart index 610e37e..e6fd60b 100644 --- a/lib/features/services/models/service_file_model.dart +++ b/lib/features/services/models/service_file_model.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:equatable/equatable.dart'; class ServiceFileModel extends Equatable { @@ -7,7 +9,8 @@ class ServiceFileModel extends Equatable { final String extension; final String url; final String serviceId; - final int fileSize; // <--- Aggiunto + final int fileSize; + final Uint8List? localBytes; const ServiceFileModel({ this.id, @@ -17,6 +20,7 @@ class ServiceFileModel extends Equatable { required this.url, required this.serviceId, required this.fileSize, + this.localBytes, }); // Trasforma i byte in qualcosa di leggibile (KB, MB, GB) @@ -39,6 +43,7 @@ class ServiceFileModel extends Equatable { String? url, String? serviceId, int? fileSize, + Uint8List? localBytes, }) { return ServiceFileModel( id: id ?? this.id, @@ -48,6 +53,7 @@ class ServiceFileModel extends Equatable { url: url ?? this.url, serviceId: serviceId ?? this.serviceId, fileSize: fileSize ?? this.fileSize, + localBytes: localBytes ?? this.localBytes, ); } diff --git a/lib/features/services/models/service_model.dart b/lib/features/services/models/service_model.dart index c980d98..df58125 100644 --- a/lib/features/services/models/service_model.dart +++ b/lib/features/services/models/service_model.dart @@ -3,6 +3,7 @@ import 'package:flux/core/utils/string_extensions.dart'; import 'package:flux/features/services/models/energy_service_model.dart'; import 'package:flux/features/services/models/entertainment_service_model.dart'; import 'package:flux/features/services/models/fin_service_model.dart'; +import 'package:flux/features/services/models/service_file_model.dart'; // <-- Aggiunto Import class ServiceModel extends Equatable { final String? id; @@ -29,6 +30,9 @@ class ServiceModel extends Equatable { final List finServices; final List entertainmentServices; + // ALLEGATI (Aggiunto) + final List files; + const ServiceModel({ this.id, this.createdAt, @@ -47,6 +51,7 @@ class ServiceModel extends Equatable { this.energyServices = const [], this.finServices = const [], this.entertainmentServices = const [], + this.files = const [], // <-- Aggiunto default vuoto this.customerDisplayName, required this.companyId, }); @@ -69,6 +74,7 @@ class ServiceModel extends Equatable { List? energyServices, List? finServices, List? entertainmentServices, + List? files, // <-- Aggiunto String? customerDisplayName, String? companyId, }) { @@ -91,6 +97,7 @@ class ServiceModel extends Equatable { finServices: finServices ?? this.finServices, entertainmentServices: entertainmentServices ?? this.entertainmentServices, + files: files ?? this.files, // <-- Aggiunto customerDisplayName: customerDisplayName ?? this.customerDisplayName, companyId: companyId ?? this.companyId, ); @@ -115,6 +122,7 @@ class ServiceModel extends Equatable { energyServices, finServices, entertainmentServices, + files, // <-- Aggiunto customerDisplayName, companyId, ]; @@ -155,6 +163,13 @@ class ServiceModel extends Equatable { .toList() ?? const [], + // I FILE! (Assicurati che la foreign key su Supabase usi esattamente questo nome) + files: + (map['service_file'] as List?) + ?.map((x) => ServiceFileModel.fromMap(x)) + .toList() ?? + const [], + // Display name del cliente con fallback customerDisplayName: map['customer'] != null ? "${map['customer']['nome'] ?? ''}".myFormat() diff --git a/lib/features/services/ui/service_form_screen/attachment_section.dart b/lib/features/services/ui/service_form_screen/attachment_section.dart index 4a31d4e..c5be3a7 100644 --- a/lib/features/services/ui/service_form_screen/attachment_section.dart +++ b/lib/features/services/ui/service_form_screen/attachment_section.dart @@ -1,7 +1,10 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/widgets/image_viewer_widget.dart'; +import 'package:flux/core/widgets/pdf_viewer_widget.dart'; import 'package:flux/features/services/blocs/services_cubit.dart'; +import 'package:flux/features/services/models/service_file_model.dart'; class AttachmentsSection extends StatelessWidget { const AttachmentsSection({super.key}); @@ -24,7 +27,7 @@ class AttachmentsSection extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - final localFiles = state.localAttachments; + final files = state.files; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -49,7 +52,7 @@ class AttachmentsSection extends StatelessWidget { ), const SizedBox(height: 12), - if (localFiles.isEmpty) + if (files.isEmpty) Container( width: double.infinity, padding: const EdgeInsets.all(24), @@ -71,42 +74,47 @@ class AttachmentsSection extends StatelessWidget { ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: localFiles.length, + itemCount: files.length, itemBuilder: (context, index) { - final file = localFiles[index]; + final file = files[index]; // Calcoliamo la dimensione in MB - final sizeMb = (file.size / (1024 * 1024)).toStringAsFixed(2); + final sizeMb = (file.fileSize / (1024 * 1024)) + .toStringAsFixed(2); // Scegliamo un'icona in base al tipo di file - final isPdf = file.extension?.toLowerCase() == 'pdf'; + final isPdf = file.extension.toLowerCase() == 'pdf'; - return Card( - margin: const EdgeInsets.only(bottom: 8), - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.grey.shade300), - ), - child: ListTile( - leading: Icon( - isPdf ? Icons.picture_as_pdf : Icons.image, - color: isPdf ? Colors.red : Colors.blue, - size: 32, + return GestureDetector( + onTap: () => _handleSingleClick(context, file), + onDoubleTap: () => _handleDoubleClick(context, file), + child: Card( + margin: const EdgeInsets.only(bottom: 8), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Colors.grey.shade300), ), - title: Text( - file.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text("$sizeMb MB"), - trailing: IconButton( - icon: const Icon( - Icons.delete_outline, - color: Colors.red, + child: ListTile( + leading: Icon( + isPdf ? Icons.picture_as_pdf : Icons.image, + color: isPdf ? Colors.red : Colors.blue, + size: 32, + ), + title: Text( + file.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text("$sizeMb MB"), + trailing: IconButton( + icon: const Icon( + Icons.delete_outline, + color: Colors.red, + ), + onPressed: () => context + .read() + .removeAttachment(index), ), - onPressed: () => context - .read() - .removeLocalAttachment(index), ), ), ); @@ -117,4 +125,54 @@ class AttachmentsSection extends StatelessWidget { }, ); } + + // --- LOGICA DI COPIA AL CLIENTE --- + void _handleSingleClick(BuildContext context, ServiceFileModel file) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text("Copia nei documenti Cliente"), + content: const Text( + "Vuoi copiare questo file nell'anagrafica del cliente? \n\n" + "Attenzione: per procedere, la pratica attuale verrà prima salvata in stato BOZZA.", + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text("Annulla"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + // 1. Diciamo al Cubit di salvare in Bozza e fare la copia + context.read().saveAndCopyFileToCustomer(file); + }, + child: const Text("Salva e Copia"), + ), + ], + ), + ); + } + + // --- LOGICA DI VISUALIZZAZIONE OVERLAY --- + void _handleDoubleClick(BuildContext context, ServiceFileModel file) { + showDialog( + context: context, + barrierDismissible: true, + builder: (ctx) => Dialog( + insetPadding: const EdgeInsets.all(16), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height * 0.8, + child: file.isPdf + ? PdfViewerWidget(url: file.url) + : ImageViewerWidget(url: file.url), + ), + ), + ), + ); + } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 9738716..2ba73bd 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,12 +7,14 @@ import Foundation import app_links import file_picker +import pdfx import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + PdfxPlugin.register(with: registry.registrar(forPlugin: "PdfxPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 646d1b6..3c09862 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -145,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.8" + extension: + dependency: transitive + description: + name: extension + sha256: be3a6b7f8adad2f6e2e8c63c895d19811fcf203e23466c6296267941d0ff4f24 + url: "https://pub.dev" + source: hosted + version: "0.6.0" fake_async: dependency: transitive description: @@ -177,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "11.0.2" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -312,6 +328,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + internet_file: + dependency: "direct main" + description: + name: internet_file + sha256: c303ebf02caa853f072c49150557e76957622adacb18420008531c97a5ef5026 + url: "https://pub.dev" + source: hosted + version: "1.3.0" intl: dependency: "direct main" description: @@ -488,6 +512,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + pdfx: + dependency: "direct main" + description: + name: pdfx + sha256: "29db9b71d46bf2335e001f91693f2c3fbbf0760e4c2eb596bf4bafab211471c1" + url: "https://pub.dev" + source: hosted + version: "2.9.2" petitparser: dependency: transitive description: @@ -496,6 +528,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.2" + photo_view: + dependency: transitive + description: + name: photo_view + sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" + url: "https://pub.dev" + source: hosted + version: "0.15.0" platform: dependency: transitive description: @@ -685,6 +725,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.12.2" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5" + url: "https://pub.dev" + source: hosted + version: "3.4.0+1" term_glyph: dependency: transitive description: @@ -709,6 +757,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + universal_file: + dependency: transitive + description: + name: universal_file + sha256: d1a957fccaad2a32023b62fe435b273ee47aaf2eb804709795e4bf4afff50960 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" url_launcher: dependency: transitive description: @@ -773,6 +837,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.5" + uuid: + dependency: transitive + description: + name: uuid + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" + url: "https://pub.dev" + source: hosted + version: "4.5.3" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 53fca7d..d399268 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,9 @@ dependencies: get_it: ^9.2.1 go_router: ^17.2.0 google_fonts: ^8.0.2 + internet_file: ^1.3.0 intl: ^0.20.2 + pdfx: ^2.9.2 shared_preferences: ^2.5.5 supabase_flutter: ^2.12.2 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 785a046..90e3d46 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { AppLinksPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + PdfxPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PdfxPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8f8ee4f..24b817c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links + pdfx url_launcher_windows )