u
This commit is contained in:
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||
import 'package:flux/features/auth/ui/auth_screen.dart';
|
||||
import 'package:flux/features/company/ui/create_company_screen.dart';
|
||||
import 'package:flux/features/customers/models/customer_model.dart';
|
||||
import 'package:flux/features/customers/ui/customer_detail_screen.dart';
|
||||
import 'package:flux/features/home_and_dashboard/ui/home_screen.dart';
|
||||
import 'package:flux/features/store/ui/create_store_screen.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@@ -58,6 +60,14 @@ class AppRouter {
|
||||
path: '/create-store',
|
||||
builder: (context, state) => const CreateStoreScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/customer/:id',
|
||||
builder: (context, state) {
|
||||
// Recuperiamo l'oggetto customer passato tramite extra
|
||||
final customer = state.extra as CustomerModel;
|
||||
return CustomerDetailScreen(customer: customer);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/core/widgets/flux_logo.dart';
|
||||
import 'package:flux/core/widgets/flux_text_field.dart';
|
||||
|
||||
@@ -6,8 +6,6 @@ import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/core/widgets/flux_text_field.dart';
|
||||
import 'package:flux/features/company/models/company_model.dart';
|
||||
import 'package:flux/features/settings/settings.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
class CreateCompanyScreen extends StatefulWidget {
|
||||
const CreateCompanyScreen({super.key});
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flux/features/customers/models/customer_file_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import '../models/customer_model.dart';
|
||||
@@ -38,7 +42,7 @@ class CustomerRepository {
|
||||
try {
|
||||
final response = await _client
|
||||
.from('customer')
|
||||
.select()
|
||||
.select('*, customer_file(count)')
|
||||
.eq('company_id', companyId)
|
||||
.eq('is_active', true)
|
||||
.order('nome');
|
||||
@@ -67,4 +71,89 @@ class CustomerRepository {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Recupera i file di un cliente specifico
|
||||
Future<List<CustomerFileModel>> getCustomerFiles(String customerId) async {
|
||||
try {
|
||||
final response = await _client
|
||||
.from('customer_file')
|
||||
.select()
|
||||
.eq('customer_id', customerId);
|
||||
|
||||
return (response as List)
|
||||
.map((f) => CustomerFileModel.fromJson(f))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw 'Errore recupero file: $e';
|
||||
}
|
||||
}
|
||||
|
||||
/// Salva il riferimento del file nel DB
|
||||
Future<void> saveFileReference(CustomerFileModel file) async {
|
||||
await _client.from('customer_file').insert(file.toJson());
|
||||
}
|
||||
|
||||
/// Carica un file e salva il riferimento nel database
|
||||
Future<CustomerFileModel> uploadAndRegisterFile({
|
||||
required String customerId,
|
||||
required PlatformFile pickedFile,
|
||||
}) async {
|
||||
try {
|
||||
final user = _client.auth.currentUser;
|
||||
if (user == null) throw 'Utente non autenticato';
|
||||
|
||||
final fileName = pickedFile.name;
|
||||
final extension = pickedFile.extension ?? '';
|
||||
final path =
|
||||
'${user.id}/$customerId/${DateTime.now().millisecondsSinceEpoch}_$fileName';
|
||||
|
||||
// Usiamo bytes invece del path per massima compatibilità
|
||||
if (pickedFile.bytes == null && pickedFile.path == null) {
|
||||
throw 'Impossibile leggere il contenuto del file';
|
||||
}
|
||||
|
||||
// Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes
|
||||
if (pickedFile.bytes != null) {
|
||||
await _client.storage
|
||||
.from('documents')
|
||||
.uploadBinary(path, pickedFile.bytes!);
|
||||
} else {
|
||||
final file = File(pickedFile.path!);
|
||||
await _client.storage.from('documents').upload(path, file);
|
||||
}
|
||||
|
||||
final String publicUrl = _client.storage
|
||||
.from('documents')
|
||||
.getPublicUrl(path);
|
||||
|
||||
final fileRecord = CustomerFileModel(
|
||||
customerId: customerId,
|
||||
name: fileName,
|
||||
url: publicUrl,
|
||||
extension: extension,
|
||||
);
|
||||
|
||||
final response = await _client
|
||||
.from('customer_file')
|
||||
.insert(fileRecord.toJson())
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return CustomerFileModel.fromJson(response);
|
||||
} catch (e) {
|
||||
throw 'Errore durante l\'upload: $e';
|
||||
}
|
||||
}
|
||||
|
||||
/// Aggiorna la lista degli URL nel database
|
||||
Future<void> updateCustomerDocuments(int id, List<String> urls) async {
|
||||
await _client.from('customer').update({'document_urls': urls}).eq('id', id);
|
||||
}
|
||||
|
||||
/// Elimina un file dallo storage
|
||||
Future<void> deleteDocument(String fullPath) async {
|
||||
// Il path dovrebbe essere ricavato dall'URL
|
||||
final path = fullPath.split('documents/').last;
|
||||
await _client.storage.from('documents').remove([path]);
|
||||
}
|
||||
}
|
||||
|
||||
45
lib/features/customers/models/customer_file_model.dart
Normal file
45
lib/features/customers/models/customer_file_model.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class CustomerFileModel extends Equatable {
|
||||
final int? id;
|
||||
final String customerId; // Riferimento UUID
|
||||
final String name;
|
||||
final String url;
|
||||
final String extension;
|
||||
final DateTime? createdAt;
|
||||
|
||||
const CustomerFileModel({
|
||||
this.id,
|
||||
required this.customerId,
|
||||
required this.name,
|
||||
required this.url,
|
||||
required this.extension,
|
||||
this.createdAt,
|
||||
});
|
||||
|
||||
factory CustomerFileModel.fromJson(Map<String, dynamic> json) {
|
||||
return CustomerFileModel(
|
||||
id: json['id'],
|
||||
customerId: json['customer_id'],
|
||||
name: json['name'],
|
||||
url: json['url'],
|
||||
extension: json['extension'] ?? '',
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
if (id != null) 'id': id,
|
||||
'customer_id': customerId,
|
||||
'name': name,
|
||||
'url': url,
|
||||
'extension': extension,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, customerId, name, url, extension, createdAt];
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class CustomerModel extends Equatable {
|
||||
final int? id; // Bigint in SQL
|
||||
final String? id; // Bigint in SQL
|
||||
final DateTime? createdAt;
|
||||
final String nome;
|
||||
final String telefono;
|
||||
@@ -11,6 +11,7 @@ class CustomerModel extends Equatable {
|
||||
final bool nonDisturbare;
|
||||
final String companyId; // UUID
|
||||
final bool isActive;
|
||||
final int fileCount;
|
||||
|
||||
const CustomerModel({
|
||||
this.id,
|
||||
@@ -23,6 +24,7 @@ class CustomerModel extends Equatable {
|
||||
this.nonDisturbare = false,
|
||||
required this.companyId,
|
||||
this.isActive = true,
|
||||
this.fileCount = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -37,10 +39,11 @@ class CustomerModel extends Equatable {
|
||||
nonDisturbare,
|
||||
companyId,
|
||||
isActive,
|
||||
fileCount,
|
||||
];
|
||||
|
||||
CustomerModel copyWith({
|
||||
int? id,
|
||||
String? id,
|
||||
DateTime? createdAt,
|
||||
String? nome,
|
||||
String? telefono,
|
||||
@@ -50,6 +53,7 @@ class CustomerModel extends Equatable {
|
||||
bool? nonDisturbare,
|
||||
String? companyId,
|
||||
bool? isActive,
|
||||
int? fileCount,
|
||||
}) {
|
||||
return CustomerModel(
|
||||
id: id ?? this.id,
|
||||
@@ -62,12 +66,18 @@ class CustomerModel extends Equatable {
|
||||
nonDisturbare: nonDisturbare ?? this.nonDisturbare,
|
||||
companyId: companyId ?? this.companyId,
|
||||
isActive: isActive ?? this.isActive,
|
||||
fileCount: fileCount ?? this.fileCount,
|
||||
);
|
||||
}
|
||||
|
||||
factory CustomerModel.fromJson(Map<String, dynamic> json) {
|
||||
int count = 0;
|
||||
if (json['customer_file'] != null &&
|
||||
(json['customer_file'] as List).isNotEmpty) {
|
||||
count = json['customer_file'][0]['count'] ?? 0;
|
||||
}
|
||||
return CustomerModel(
|
||||
id: json['id'],
|
||||
id: json['id'] as String,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'])
|
||||
: null,
|
||||
@@ -79,8 +89,9 @@ class CustomerModel extends Equatable {
|
||||
? DateTime.parse(json['data_ultimo_contatto'])
|
||||
: null,
|
||||
nonDisturbare: json['non_disturbare'] ?? false,
|
||||
companyId: json['company_id'],
|
||||
companyId: json['company_id'] as String,
|
||||
isActive: json['is_active'] ?? true,
|
||||
fileCount: count,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
271
lib/features/customers/ui/customer_detail_screen.dart
Normal file
271
lib/features/customers/ui/customer_detail_screen.dart
Normal file
@@ -0,0 +1,271 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/features/customers/data/customer_repository.dart';
|
||||
import 'package:flux/features/customers/models/customer_model.dart';
|
||||
import 'package:flux/features/customers/models/customer_file_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
class CustomerDetailScreen extends StatefulWidget {
|
||||
final CustomerModel customer;
|
||||
const CustomerDetailScreen({super.key, required this.customer});
|
||||
|
||||
@override
|
||||
State<CustomerDetailScreen> createState() => _CustomerDetailScreenState();
|
||||
}
|
||||
|
||||
class _CustomerDetailScreenState extends State<CustomerDetailScreen> {
|
||||
final _repository = GetIt.I<CustomerRepository>();
|
||||
List<CustomerFileModel> _files = [];
|
||||
bool _isLoadingFiles = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadFiles();
|
||||
}
|
||||
|
||||
Future<void> _loadFiles() async {
|
||||
try {
|
||||
final files = await _repository.getCustomerFiles(
|
||||
widget.customer.id.toString(),
|
||||
);
|
||||
setState(() {
|
||||
_files = files;
|
||||
_isLoadingFiles = false;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _isLoadingFiles = false);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(e.toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickAndUpload() async {
|
||||
// Chiamata statica pulita
|
||||
FilePickerResult? result = await FilePicker.pickFiles(
|
||||
allowMultiple: true,
|
||||
type: FileType.any,
|
||||
withData: true, // Fondamentale per avere i bytes pronti se servono
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
for (var pickedFile in result.files) {
|
||||
try {
|
||||
final newFile = await _repository.uploadAndRegisterFile(
|
||||
customerId: widget.customer.id.toString(),
|
||||
pickedFile: pickedFile,
|
||||
);
|
||||
setState(() => _files.add(newFile));
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Errore upload ${pickedFile.name}: $e")),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: context.background,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.customer.nome,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
backgroundColor: context.background,
|
||||
elevation: 0,
|
||||
),
|
||||
body: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// COLONNA SINISTRA: ANAGRAFICA
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: _buildInfoSection(),
|
||||
),
|
||||
),
|
||||
|
||||
// DIVISORE VERTICALE
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
color: context.accent.withValues(alpha: 0.1),
|
||||
),
|
||||
|
||||
// COLONNA DESTRA: DOCUMENTI
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: _buildDocumentSection(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoSection() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_infoTile(Icons.phone_android, "Telefono", widget.customer.telefono),
|
||||
_infoTile(
|
||||
Icons.email_outlined,
|
||||
"Email",
|
||||
widget.customer.email.isEmpty ? "Non fornita" : widget.customer.email,
|
||||
),
|
||||
_infoTile(
|
||||
Icons.notes_outlined,
|
||||
"Note",
|
||||
widget.customer.note.isEmpty
|
||||
? "Nessun appunto"
|
||||
: widget.customer.note,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (widget.customer.nonDisturbare)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.privacy_tip, color: Colors.red, size: 20),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
"PRIVACY: Non disturbare",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDocumentSection() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"DOCUMENTI",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.accent,
|
||||
),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _pickAndUpload,
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
label: const Text("CARICA FILE"),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (_isLoadingFiles)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
else if (_files.isEmpty)
|
||||
const Center(child: Text("Nessun documento presente"))
|
||||
else
|
||||
Expanded(
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
childAspectRatio: 1.2,
|
||||
),
|
||||
itemCount: _files.length,
|
||||
itemBuilder: (context, index) => _FileCard(file: _files[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoTile(IconData icon, String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(icon, size: 18, color: context.accent),
|
||||
const SizedBox(width: 8),
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(color: context.secondaryText, fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FileCard extends StatelessWidget {
|
||||
final CustomerFileModel file;
|
||||
const _FileCard({required this.file});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.background,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: context.accent.withValues(alpha: 0.1)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(_getFileIcon(file.extension), size: 48, color: context.accent),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
file.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
IconData _getFileIcon(String ext) {
|
||||
switch (ext.toLowerCase()) {
|
||||
case 'pdf':
|
||||
return Icons.picture_as_pdf;
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
case 'png':
|
||||
return Icons.image;
|
||||
default:
|
||||
return Icons.insert_drive_file;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/features/customers/blocs/customer_bloc.dart';
|
||||
import 'package:flux/features/customers/models/customer_model.dart';
|
||||
import 'package:flux/features/customers/ui/customer_form.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class CustomersContent extends StatefulWidget {
|
||||
const CustomersContent({super.key});
|
||||
@@ -147,7 +148,10 @@ class _CustomersContentState extends State<CustomersContent> {
|
||||
final customer = state.customers[index];
|
||||
return _CustomerTile(
|
||||
customer: customer,
|
||||
onTap: () => _openCustomerForm(customer: customer),
|
||||
onTap: () => context.push(
|
||||
'/customer/${customer.id}',
|
||||
extra: customer,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -222,6 +226,27 @@ class _CustomerTile extends StatelessWidget {
|
||||
customer.telefono,
|
||||
style: TextStyle(color: context.secondaryText),
|
||||
),
|
||||
if (customer.email.isNotEmpty) ...[
|
||||
Text(' - ', style: TextStyle(color: context.secondaryText)),
|
||||
Icon(Icons.email, size: 14, color: context.secondaryText),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
customer.email,
|
||||
style: TextStyle(color: context.secondaryText),
|
||||
),
|
||||
],
|
||||
if (customer.fileCount > 0) ...[
|
||||
Text(' - ', style: TextStyle(color: context.secondaryText)),
|
||||
Icon(Icons.attach_file, size: 14, color: context.accent),
|
||||
Text(
|
||||
'${customer.fileCount} doc',
|
||||
style: TextStyle(
|
||||
color: context.accent,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/features/settings/settings.dart';
|
||||
import 'package:flux/features/store/bloc/store_bloc.dart';
|
||||
import 'package:flux/features/store/models/store_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/core/widgets/flux_text_field.dart';
|
||||
@@ -23,7 +21,6 @@ class _CreateStoreScreenState extends State<CreateStoreScreen> {
|
||||
final _capController = TextEditingController();
|
||||
final _comuneController = TextEditingController();
|
||||
final _provinciaController = TextEditingController();
|
||||
final AppSettings _settings = GetIt.I<AppSettings>();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
||||
@@ -6,11 +6,13 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import app_links
|
||||
import file_picker
|
||||
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"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
PODS:
|
||||
- app_links (6.4.1):
|
||||
- FlutterMacOS
|
||||
- file_picker (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
@@ -10,6 +12,7 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
@@ -17,6 +20,8 @@ DEPENDENCIES:
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
|
||||
file_picker:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
shared_preferences_foundation:
|
||||
@@ -26,6 +31,7 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f
|
||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
|
||||
|
||||
@@ -10,5 +10,13 @@
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -6,5 +6,13 @@
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
40
pubspec.lock
40
pubspec.lock
@@ -105,6 +105,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.5+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -121,6 +129,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.12"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -153,6 +169,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: f13a03000d942e476bc1ff0a736d2e9de711d2f89a95cd4c1d88f861c3348387
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -174,6 +198,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.34"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -797,6 +829,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -8,6 +8,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
equatable: ^2.0.8
|
||||
file_picker: ^11.0.2
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_bloc: ^9.1.1
|
||||
|
||||
Reference in New Issue
Block a user