1

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,
                    ),
                   
0

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.