I am doing an app in flutter and I try to upload image to firebase but I get error each time.flutter: Starting upload for: image_1745500221837.jpg
flutter: File path: /Users/elenaraluca/Library/Developer/CoreSimulator/Devices/E232FCD7-2514-41A9-A7A2-EE88C7FB9EE7/data/Containers/Data/Application/5A6D4724-4F96-4371-916B-05AA87947EF8/tmp/image_picker_6FA7ECB6-7FCE-407A-AD9B-3BEFF85F5C37-43689-000004ABB846EA84.jpg
flutter: File size: 0.11029911041259766MB
flutter: Upload attempt 1
flutter: Upload progress: 0.13%
flutter: Upload progress: 0.13%
flutter: Upload attempt 1 failed: [firebase_storage/unknown] cannot parse response
flutter: Upload stream error: [firebase_storage/unknown] cannot parse response
flutter: Upload attempt 2
flutter: Upload progress: 0.13%
flutter: Upload progress: 0.13%
flutter: Upload attempt 2 failed: [firebase_storage/unknown] cannot parse response
flutter: Upload stream error: [firebase_storage/unknown] cannot parse response
flutter: Upload attempt 3
flutter: Upload progress: 0.13%
flutter: Upload progress: 0.13%
flutter: Upload attempt 3 failed: [firebase_storage/unknown] cannot parse response
flutter: Upload error occurred:
flutter: Error type: FirebaseException
flutter: Error details: [firebase_storage/unknown] cannot parse response
flutter: Firebase error code: unknown
flutter: Firebase error message: cannot parse response
flutter: Error uploading image 0: [firebase_storage/unknown] cannot parse response
flutter: Error details: [firebase_storage/unknown] cannot parse response
flutter: Upload stream error: [firebase_storage/unknown] cannot parse response
This is the code I used for the page. User has to introduce location , type of incident and image of it. The issue it's probably from how iniate to be stored in Firestore as image url and storage?
class ReportIncidentPage extends StatefulWidget {
const ReportIncidentPage({super.key});
@override
State<ReportIncidentPage> createState() => _ReportIncidentPageState();
}
class _ReportIncidentPageState extends State<ReportIncidentPage> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseStorage _storage = FirebaseStorage.instance;
final TextEditingController _descriptionController = TextEditingController();
final ImagePicker _picker = ImagePicker();
List<File> _selectedImages = [];
String selectedIncidentType = 'Selectează tipul incidentului';
String selectedLocation = 'Selectează orașul și județul';
String? manualAddress;
String? mapAddress;
bool isManualAddress = false;
bool isMapLocation = false;
bool _isSubmitting = false;
bool _isUploadingImages = false;
Future<String> _generateIncidentNumber() async {
final now = DateTime.now();
final year = now.year;
final month = now.month.toString().padLeft(2, '0');
final day = now.day.toString().padLeft(2, '0');
// Get the count of incidents for today
final startOfDay = DateTime(now.year, now.month, now.day);
final endOfDay = startOfDay.add(const Duration(days: 1));
final snapshot = await _firestore
.collection('incidents')
.where('reportedAt', isGreaterThanOrEqualTo: startOfDay)
.where('reportedAt', isLessThan: endOfDay)
.count()
.get();
final count = snapshot.count ?? 0;
final sequence = (count + 1).toString().padLeft(3, '0');
return 'INC-$year$month$day-$sequence';
}
Future<void> _pickImages() async {
try {
final List<XFile> pickedFiles = await _picker.pickMultiImage(
maxWidth: 1920,
maxHeight: 1080,
imageQuality: 85,
);
if (pickedFiles.isNotEmpty) {
setState(() {
_selectedImages.addAll(pickedFiles.map((file) => File(file.path)));
});
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Eroare la selectarea imaginilor: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
void _removeImage(int index) {
setState(() {
_selectedImages.removeAt(index);
});
}
@override
void dispose() {
_descriptionController.dispose();
super.dispose();
}
Future<String> _uploadSingleImage(File image, String path) async {
try {
// Create a simple filename
final String fileName = 'image_${DateTime.now().millisecondsSinceEpoch}.jpg';
final Reference storageRef = _storage.ref().child('incidents/photos/$fileName');
print('Starting upload for: $fileName');
print('File path: ${image.path}');
// Get file size
final int fileSize = await image.length();
print('File size: ${fileSize / 1024 / 1024}MB');
// Basic metadata
final metadata = SettableMetadata(
contentType: 'image/jpeg',
customMetadata: {
'size': fileSize.toString(),
'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
},
);
// Upload with retry logic
int retryCount = 0;
const maxRetries = 3;
while (retryCount < maxRetries) {
try {
print('Upload attempt ${retryCount + 1}');
// Upload file
final uploadTask = storageRef.putFile(image, metadata);
// Monitor progress
uploadTask.snapshotEvents.listen(
(TaskSnapshot snapshot) {
if (snapshot.totalBytes > 0) {
final progress = snapshot.bytesTransferred / snapshot.totalBytes;
print('Upload progress: ${(progress * 100).toStringAsFixed(2)}%');
}
},
onError: (error) {
print('Upload stream error: $error');
},
);
// Wait for completion
final snapshot = await uploadTask;
print('Upload completed with state: ${snapshot.state}');
if (snapshot.state == TaskState.success) {
// Get download URL
final downloadUrl = await snapshot.ref.getDownloadURL();
print('Download URL obtained: $downloadUrl');
return downloadUrl;
} else {
throw Exception('Upload failed with state: ${snapshot.state}');
}
} catch (e) {
retryCount++;
print('Upload attempt $retryCount failed: $e');
if (retryCount == maxRetries) {
rethrow;
}
// Wait before retrying
await Future.delayed(Duration(seconds: 1 * retryCount));
}
}
throw Exception('All upload attempts failed');
} catch (e) {
print('Upload error occurred:');
print('Error type: ${e.runtimeType}');
print('Error details: ${e.toString()}');
if (e is FirebaseException) {
print('Firebase error code: ${e.code}');
print('Firebase error message: ${e.message}');
}
rethrow;
}
}
Future<List<String>> _uploadImages(String incidentNumber) async {
List<String> imageUrls = [];
setState(() {
_isUploadingImages = true;
});
try {
for (int i = 0; i < _selectedImages.length; i++) {
try {
print('Starting upload for image $i');
final String fileName = 'incidents/$incidentNumber/image_$i.jpg';
final String downloadUrl = await _uploadSingleImage(_selectedImages[i], fileName);
imageUrls.add(downloadUrl);
print('Successfully uploaded image $i: $downloadUrl');
} catch (e) {
print('Error uploading image $i: $e');
print('Error details: ${e.toString()}');
// Show error to user
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Eroare la încărcarea fotografiei ${i + 1}: $e'),
backgroundColor: Colors.red,
),
);
}
// Continue with other images even if one fails
}
}
} catch (e) {
print('Error in upload process: $e');
print('Error details: ${e.toString()}');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Eroare la procesul de încărcare: $e'),
backgroundColor: Colors.red,
),
);
}
} finally {
setState(() {
_isUploadingImages = false;
});
}
return imageUrls;
}
Future<bool> _checkInstitutionExists(String cityAndCounty) async {
try {
print('Checking institution for: $cityAndCounty');
// First, let's get all institutions to debug
final allInstitutions = await _firestore
.collection('institutes')
.get();
print('Total institutions in database: ${allInstitutions.docs.length}');
// Print all institutions for debugging
for (var doc in allInstitutions.docs) {
print('Institution data: ${doc.data()}');
}
// Try the exact match query
final querySnapshot = await _firestore
.collection('institutes')
.where('cityAndCounty', isEqualTo: cityAndCounty)
.get();
print('Found ${querySnapshot.docs.length} institutions with exact match');
if (querySnapshot.docs.isNotEmpty) {
print('Institution found: ${querySnapshot.docs.first.data()}');
// Check if the institution is active in subscription
final institution = querySnapshot.docs.first.data();
if (institution['subscription'] != null &&
institution['subscription']['status'] == 'active') {
return true;
} else {
print('Institution found but subscription status is: ${institution['subscription']?['status']}');
}
}
// If no exact match, try a more flexible approach
final flexibleQuery = await _firestore
.collection('institutes')
.where('cityAndCounty', isGreaterThanOrEqualTo: cityAndCounty)
.where('cityAndCounty', isLessThanOrEqualTo: cityAndCounty + '\uf8ff')
.get();
print('Found ${flexibleQuery.docs.length} institutions with flexible match');
if (flexibleQuery.docs.isNotEmpty) {
print('Institution found with flexible match: ${flexibleQuery.docs.first.data()}');
// Check if the institution is active in subscription
final institution = flexibleQuery.docs.first.data();
if (institution['subscription'] != null &&
institution['subscription']['status'] == 'active') {
return true;
} else {
print('Institution found but subscription status is: ${institution['subscription']?['status']}');
}
}
return false;
} catch (e) {
print('Error checking institution: $e');
return false;
}
}
Future<void> _submitIncident() async {
// Check if incident type is selected
if (selectedIncidentType == 'Selectează tipul incidentului') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Vă rugăm să selectați tipul incidentului'),
backgroundColor: Colors.red,
),
);
return;
}
// Check if description is provided
if (_descriptionController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Vă rugăm să adăugați o descriere'),
backgroundColor: Colors.red,
),
);
return;
}
// Check if at least one photo is selected
if (_selectedImages.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Vă rugăm să adăugați cel puțin o fotografie'),
backgroundColor: Colors.red,
),
);
return;
}
// Check location validation
bool hasValidLocation = false;
String locationError = '';
if (isManualAddress) {
// Check manual address
if (manualAddress != null && manualAddress!.isNotEmpty) {
hasValidLocation = true;
} else {
locationError = 'Vă rugăm să introduceți adresa manual';
}
} else if (isMapLocation) {
// Check if location is selected from map
if (mapAddress != null && mapAddress!.isNotEmpty) {
hasValidLocation = true;
} else {
locationError = 'Vă rugăm să selectați o locație pe hartă';
}
}
// Check if city and county are selected
if (selectedLocation == 'Selectează orașul și județul') {
hasValidLocation = false;
locationError = 'Vă rugăm să selectați orașul și județul';
}
if (!hasValidLocation) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(locationError),
backgroundColor: Colors.red,
),
);
return;
}
// Check if there's an active institution in the selected city
final hasInstitution = await _checkInstitutionExists(selectedLocation);
if (!hasInstitution) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Nu există nicio instituție înregistrată în ${selectedLocation}. Vă rugăm să contactați administratorul.'),
backgroundColor: Colors.red,
),
);
return;
}
setState(() {
_isSubmitting = true;
});
try {
final User? user = _auth.currentUser;
if (user == null) {
throw Exception('Utilizatorul nu este autentificat');
}
// Generate incident number
final incidentNumber = await _generateIncidentNumber();
// First, upload all images and get their URLs
final List<String> imageUrls = await _uploadImages(incidentNumber);
// Verify that we have uploaded images
if (imageUrls.isEmpty) {
throw Exception('Nu s-au putut încărca fotografiile. Vă rugăm să încercați din nou.');
}
print('Uploaded ${imageUrls.length} images: $imageUrls');
// Create incident data with the photo URLs
final incidentData = {
'incidentNumber': incidentNumber,
'type': selectedIncidentType,
'location': isMapLocation ? mapAddress : (isManualAddress ? manualAddress : null),
'address': isManualAddress ? manualAddress : (isMapLocation ? mapAddress : null),
'cityAndCounty': selectedLocation,
'status': 'În procesare',
'reportedBy': user.uid,
'reportedAt': FieldValue.serverTimestamp(),
'updatedAt': FieldValue.serverTimestamp(),
'description': _descriptionController.text,
'photos': imageUrls,
};
// Add to incidents collection
final incidentRef = await _firestore.collection('incidents').add(incidentData);
// Add to user's reported incidents subcollection
await _firestore
.collection('users')
.doc(user.uid)
.collection('reported_incidents')
.doc(incidentRef.id)
.set({
'incidentId': incidentRef.id,
'incidentNumber': incidentNumber,
'type': selectedIncidentType,
'location': isMapLocation ? mapAddress : (isManualAddress ? manualAddress : null),
'address': isManualAddress ? manualAddress : (isMapLocation ? mapAddress : null),
'cityAndCounty': selectedLocation,
'status': 'În procesare',
'description': _descriptionController.text,
'photos': imageUrls,
'reportedAt': FieldValue.serverTimestamp(),
});
// Update user's incidents count
await _firestore.collection('users').doc(user.uid).update({
'incidents': FieldValue.increment(1),
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Incidentul a fost raportat cu succes'),
backgroundColor: Colors.green,
),
);
Navigator.pop(context);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Eroare la raportarea incidentului: $e'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() {
_isSubmitting = false;
});
}
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDarkMode = theme.brightness == Brightness.dark;
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar(
backgroundColor: theme.cardColor,
elevation: 0,
title: Text(
'Raportează un incident',
style: GoogleFonts.inter(
color: theme.textTheme.titleLarge?.color,
fontWeight: FontWeight.w600,
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: theme.iconTheme.color),
onPressed: () => Navigator.pop(context),
),
flexibleSpace: Container(
decoration: BoxDecoration(
color: theme.cardColor,
),
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tipul incidentului *',
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: theme.textTheme.titleLarge?.color,
),
),
const SizedBox(height: 8),
InkWell(
onTap: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const IncidentTypeSelectorPage(),
),
);
if (result != null) {
setState(() {
selectedIncidentType = result;
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: theme.inputDecorationTheme.fillColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.inputDecorationTheme.enabledBorder?.borderSide.color ?? Colors.grey[300]!,
width: 1,
),
),
child: Row(
children: [
Expanded(
child: Text(
selectedIncidentType,
style: GoogleFonts.inter(
color: theme.textTheme.bodyMedium?.color,
fontSize: 14,
),
),
),
Icon(Icons.keyboard_arrow_down, color: theme.textTheme.bodyMedium?.color),
],
),
),
),
const SizedBox(height: 24),
Text(
'Locația *',
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: theme.textTheme.titleLarge?.color,
),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const MapLocationPickerPage(),
),
);
if (result != null) {
setState(() {
mapAddress = result['address'];
isMapLocation = true;
});
}
},
icon: Icon(Icons.map_outlined, color: theme.textTheme.bodyMedium?.color),
label: Text(
'Selectează pe hartă *',
style: GoogleFonts.inter(color: theme.textTheme.bodyMedium?.color),
),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
side: BorderSide(color: theme.inputDecorationTheme.enabledBorder?.borderSide.color ?? Colors.grey[300]!),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: () {
setState(() {
isManualAddress = true;
mapAddress = null;
});
},
icon: Icon(Icons.edit_location_outlined, color: theme.textTheme.bodyMedium?.color),
label: Text(
'Adaugă adresa *',
style: GoogleFonts.inter(color: theme.textTheme.bodyMedium?.color),
),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
side: BorderSide(color: theme.inputDecorationTheme.enabledBorder?.borderSide.color ?? Colors.grey[300]!),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
if (isMapLocation && mapAddress != null) ...[
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.inputDecorationTheme.fillColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.inputDecorationTheme.enabledBorder?.borderSide.color ?? Colors.grey[300]!,
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.location_on_outlined,
size: 20,
color: const Color(0xFFFFC107),
),
const SizedBox(width: 8),
Expanded(
child: Text(
mapAddress!,
style: GoogleFonts.inter(
color: theme.textTheme.bodyMedium?.color,
fontSize: 14,
),
),
),
],
),
),
],
if (isManualAddress) ...[
const SizedBox(height: 16),
TextField(
onChanged: (value) {
setState(() {
manualAddress = value;
});
},
decoration: InputDecoration(
hintText: 'Introdu adresa completă *',
hintStyle: GoogleFonts.inter(color: theme.textTheme.bodyMedium?.color),
filled: true,
fillColor: theme.scaffoldBackgroundColor,
border: theme.inputDecorationTheme.border,
enabledBorder: theme.inputDecorationTheme.enabledBorder,
focusedBorder: theme.inputDecorationTheme.focusedBorder,
),
maxLines: 2,
),
],
const SizedBox(height: 16),
Text(
'Orașul și județul *',
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: theme.textTheme.titleLarge?.color,
),
),
const SizedBox(height: 8),
InkWell(
onTap: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LocationSelectorPage(),
),
);
if (result != null) {
setState(() {
selectedLocation = result;
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: theme.inputDecorationTheme.fillColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.inputDecorationTheme.enabledBorder?.borderSide.color ?? Colors.grey[300]!,
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.location_on_outlined,
size: 20,
color: theme.textTheme.bodyMedium?.color,
),