Lewati ke isi

Modul Pertemuan 5: Firebase Storage & Upload Foto Absensi

🎯 Tujuan Pembelajaran

  1. Siswa mampu mengaktifkan dan mengamankan layanan Firebase Storage.

  2. Siswa mampu mengakses Kamera HP menggunakan package image_picker.

  3. Siswa mampu melakukan kompresi (pengecilan ukuran) gambar sebelum diunggah.

  4. Siswa mampu mengunggah file ke Firebase Storage dan menyimpan link (URL) gambarnya ke Cloud Firestore.


1. Setup Firebase Storage & Rules

Sama seperti Firestore, kita harus menyiapkan "gudang" penyimpanan file-nya terlebih dahulu.

Langkah-langkah:

  1. Buka dashboard project Firebase kalian.

  2. Di menu kiri, pilih Build -> Storage.

  3. Klik Get Started (Mulai).

  4. Pilih Start in production mode, lalu klik Next.

  5. Pilih lokasi server (samakan dengan lokasi Firestore sebelumnya, misal: asia-southeast2), lalu klik Done.

  6. Setelah gudang penyimpanan jadi, masuk ke tab Rules (Aturan).

  7. Ubah aturannya menjadi seperti ini agar aman:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // Hanya user yang login yang bisa upload dan lihat foto
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
  1. Klik Publish.

2. Instalasi Package Tambahan

Buka terminal VS Code, lalu jalankan perintah ini untuk menambahkan fitur Storage dan fitur Kamera:

flutter pub add firebase_storage image_picker


3. Konfigurasi Izin Akses (Permissions)

Agar aplikasi kita tidak crash (menutup sendiri) saat mencoba membuka kamera HP, kita harus meminta izin secara resmi ke sistem operasi Android dan iOS.

Untuk Android: Biasanya untuk package terbaru, izin Android sudah otomatis ditangani. Namun, pastikan minSdkVersion di file android/app/build.gradle minimal diset ke 21.

Untuk iOS (Khusus pengguna Mac): Buka file ios/Runner/Info.plist, tambahkan kode berikut di dalam tag <dict>:

<key>NSCameraUsageDescription</key>
<string>Aplikasi ini membutuhkan akses kamera untuk foto bukti PKL.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Aplikasi ini membutuhkan akses galeri untuk foto bukti PKL.</string>

4. Logika Upload di Provider

Buka kembali file lib/providers/attendance_provider.dart yang kita buat di Pertemuan 4.

Kita akan menambahkan dua fitur: mengambil foto dengan ukuran kecil, dan mengunggahnya ke Storage.

Tambahkan kode ini di dalam class AttendanceProvider:

import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
// ... import firestore yang sudah ada

// 1. Inisialisasi Firebase Storage dan Image Picker
final FirebaseStorage _storage = FirebaseStorage.instance;
final ImagePicker _picker = ImagePicker();

// 2. Fungsi Mengambil & Mengompres Gambar
Future<File?> pickAndCompressImage() async {
  // Trik Kompresi: Gunakan parameter imageQuality (0 - 100).
  // Angka 30 sudah cukup bagus untuk absen tapi ukurannya sangat kecil (ringan diupload)
  final XFile? pickedFile = await _picker.pickImage(
    source: ImageSource.camera, 
    imageQuality: 30, 
  );

  if (pickedFile != null) {
    return File(pickedFile.path);
  }
  return null; // Jika user batal memotret
}

// 3. Fungsi Upload Foto ke Storage
Future<String> uploadPhotoToStorage(File imageFile, String uid) async {
  try {
    // Membuat nama file unik berdasarkan waktu saat ini
    String fileName = DateTime.now().millisecondsSinceEpoch.toString();

    // Menentukan lokasi folder di Storage: attendances/uid_siswa/namafile.jpg
    Reference ref = _storage.ref().child('attendances/$uid/$fileName.jpg');

    // Proses Upload
    UploadTask uploadTask = ref.putFile(imageFile);
    TaskSnapshot snapshot = await uploadTask;

    // Minta URL/Link dari gambar yang berhasil diupload
    String downloadUrl = await snapshot.ref.getDownloadURL();
    return downloadUrl;
  } catch (e) {
    throw Exception('Gagal mengupload foto: $e');
  }
}

5. Menyatukan Firestore dan Storage

Terakhir, kita ubah fungsi addAttendance yang lama, agar menerima URL gambar dan menyimpannya bersamaan dengan data teks.

Ubah fungsi addAttendance menjadi seperti ini:

Future<void> addAttendance(String uid, String status, String note, File imageFile) async {
  try {
    // 1. Upload gambarnya dulu ke Storage, tunggu sampai dapat URL-nya
    String photoUrl = await uploadPhotoToStorage(imageFile, uid);

    // 2. Simpan semua data (termasuk link foto) ke Firestore NoSQL
    await _firestore.collection('attendances').add({
      'student_id': uid,       
      'date': Timestamp.now(), 
      'status': status,        
      'activity_note': note,   
      'photo_url': photoUrl,   // Field baru untuk menyimpan link foto!
    });

    notifyListeners();
  } catch (e) {
    throw Exception('Gagal menyimpan absensi: $e');
  }
}

6. Menerapkan di UI & Tanda Loading

Pada form pengisian absen (attendance_form_screen.dart), pastikan kalian memberikan indikator loading (berputar).

Proses mengunggah foto membutuhkan waktu (tergantung koneksi internet). Jika tidak ada loading, siswa mungkin akan memencet tombol Submit berkali-kali!

// Contoh logika saat tombol submit ditekan:
setState(() {
  isLoading = true; // Munculkan efek loading di layar
});

try {
  await context.read<AttendanceProvider>().addAttendance(
    userUid, 
    status, 
    catatan, 
    fileFotoPilihanSiswa!
  );
  // Berhasil? Tutup halaman / kembali ke Dashboard
  context.pop(); 
} catch (e) {
  // Tampilkan pesan error
} finally {
  setState(() {
    isLoading = false; // Matikan efek loading
  });
}

🎉 Selamat! Modul Selesai!

Kalian telah berhasil membangun aplikasi Backend modern (BaaS) secara utuh. Mulai dari sistem Autentikasi, Database Real-time (NoSQL), hingga penyimpanan File di Cloud.

Ilmu ini adalah standar industri Startup saat ini. Jika kalian menguasainya, kalian sangat siap untuk membuat aplikasi Freelance, Tugas Akhir, maupun melamar pekerjaan sebagai Junior Mobile Developer!