✦ Selamat Idul Fitri 1447 H 🌙 Taqabbalallahu minna wa minkum. Mohon maaf lahir dan batin. ✦
ALFAkwt

Cara Kurangi Ukuran APK Flutter dari 25 MB Jadi di Bawah 8 MB: Panduan Lengkap

· 0 komentar · ± 7 menit baca · 👁 301 dilihat
Perbandingan ukuran APK Flutter sebelum dan sesudah optimasi

Dua puluh lima megabyte. Itu ukuran APK pertama yang saya hasilkan dari proyek Flutter — proyek yang kontennya tidak lebih dari teks dan beberapa ikon sederhana. Tidak ada video, tidak ada animasi berat, tidak ada fitur kamera. Hanya teks. Dan 25 MB.

Saya duduk beberapa menit menatap angka itu di output terminal. Kemudian mulai membongkar semuanya.

Dua minggu kemudian, APK yang sama berjalan di 6.8 MB. Artikel ini adalah catatan lengkap dari proses itu — teknik demi teknik, dengan penjelasan mengapa masing-masing bekerja dan dalam kondisi apa masing-masing relevan untuk diterapkan.


Mengapa Ukuran APK Penting?

Sebelum masuk ke teknis, saya ingin menetapkan konteks. Ada dua alasan utama mengapa ukuran APK adalah metrik yang perlu diperhatikan serius.

Pertama, conversion rate instalasi. Penelitian internal Google menunjukkan bahwa setiap penambahan 6 MB ukuran APK berkorelasi dengan penurunan instalasi sekitar 1%. Untuk aplikasi yang menarget jutaan pengguna, ini angka yang sangat signifikan.

Kedua, aksesibilitas perangkat. Di Indonesia, banyak pengguna masih menggunakan HP dengan storage internal 8 atau 16 GB. Sebagian besar sudah terisi sistem operasi dan aplikasi bawaan. Ruang kosong untuk aplikasi baru sangat terbatas. APK yang ramping adalah bentuk rasa hormat kepada pengguna.


Langkah 1: Analisis Komposisi APK

Sebelum mengoptimasi, Anda perlu tahu apa yang menyumbang ukuran terbesar. Flutter menyediakan perintah untuk ini:

flutter build apk --analyze-size

Perintah ini menghasilkan file apk-code-size-analysis.json yang bisa dibuka di Flutter DevTools menggunakan tab "App Size".

Dari situ Anda akan melihat breakdown ukuran per komponen: Flutter engine, Dart code, native libraries, aset, dan sebagainya. Di proyek saya, distribusinya seperti ini sebelum optimasi:

  • Flutter engine: ~4.5 MB (tidak bisa dikurangi)
  • Dart compiled code: ~8.2 MB ← target utama
  • Native libraries (arm64 + armeabi): ~7.1 MB ← target utama
  • Aset (gambar, font): ~4.8 MB ← target utama
  • Resource Android: ~0.4 MB

Dari analisis ini, tiga area yang bisa dioptimasi sudah jelas.


Langkah 2: Build dengan --split-per-abi

Ini adalah optimasi tunggal dengan dampak terbesar. Secara default, Flutter menghasilkan APK "fat" yang berisi kode native untuk semua arsitektur: arm64-v8a, armeabi-v7a, dan kadang x86_64.

Padahal setiap perangkat hanya butuh satu arsitektur. Pengguna HP Samsung Galaxy A-series butuh arm64-v8a. Pengguna HP lama dengan chip 32-bit butuh armeabi-v7a. Mereka tidak perlu keduanya dalam satu APK yang sama.

Solusinya adalah split per ABI:

flutter build apk --split-per-abi --release

Ini menghasilkan tiga file terpisah:

build/app/outputs/apk/release/
├── app-arm64-v8a-release.apk     (~6.8 MB)
├── app-armeabi-v7a-release.apk (~6.2 MB)
└── app-x86_64-release.apk (~7.1 MB)

Upload ketiganya ke Google Play Console, dan Play Store akan otomatis menyajikan APK yang tepat ke setiap perangkat. Pengguna mendapatkan file yang lebih kecil, Anda mendapatkan conversion rate yang lebih baik.


Langkah 3: Aktifkan Obfuscation dan Tree Shaking

Dart compiler sudah melakukan tree shaking (menghilangkan kode yang tidak dipakai) secara default saat build release. Tapi ada tambahan yang perlu dilakukan secara eksplisit:

flutter build apk --release 
--obfuscate
--split-debug-info=build/debug-info
--split-per-abi

Flag --obfuscate mengubah nama class dan method Dart menjadi nama acak yang pendek, mengurangi ukuran compiled code secara signifikan. Flag --split-debug-info memindahkan debug symbols ke file terpisah — debug symbols ini sering menyumbang 1–3 MB yang tidak perlu ada di APK produksi.

Penting: Simpan file dari folder build/debug-info dengan aman. Anda akan membutuhkannya untuk men-deobfuscate stack trace jika ada crash report dari pengguna. Tanpa file ini, crash report dari Firebase Crashlytics atau Sentry akan tidak terbaca.


Langkah 4: Audit dan Pangkas Dependency

Setiap package yang Anda tambahkan ke pubspec.yaml membawa kode-nya sendiri ke dalam APK. Banyak developer menambahkan package tanpa mempertimbangkan tradeoff ukuran.

Jalankan perintah ini untuk melihat semua dependency yang terinstall:

flutter pub deps --style=tree

Kemudian tanyakan untuk setiap dependency: apakah benar-benar dibutuhkan? Apakah bisa digantikan dengan implementasi custom yang lebih ringan?

Beberapa substitusi yang saya lakukan di proyek saya:

  • flutter_html → Custom regex-based HTML renderer (hemat ~1.2 MB)
  • cached_network_image → Tidak perlu (semua konten offline)
  • get atau providersetState sederhana untuk state kecil, InheritedWidget untuk shared state

Setiap 100 KB yang bisa dihilangkan dari dependency adalah 100 KB yang tidak perlu didownload pengguna.


Langkah 5: Optimasi Aset Gambar

Gambar adalah sumber ukuran APK yang paling mudah dioptimasi dan paling sering diabaikan.

Gunakan WebP, bukan PNG. Format WebP memberikan kompresi 25–34% lebih baik dari PNG untuk kualitas visual yang sama. Untuk gambar dengan transparansi pun, WebP lossy dengan kualitas 85–90 biasanya tidak terdeteksi perbedaannya oleh mata.

Konversi batch menggunakan cwebp:

# Install: sudo apt install webp (Linux) atau brew install webp (Mac)
for file in assets/images/*.png; do
cwebp -q 85 "$file" -o "${file%.png}.webp"
done

Deklarasikan hanya apa yang dibutuhkan di pubspec.yaml. Banyak developer menulis:

flutter:
assets:
- assets/images/

Ini menyertakan semua file di folder tersebut, termasuk file .DS_Store, thumbnail, dan file yang tidak dipakai. Deklarasikan file satu per satu, atau setidaknya pastikan folder tersebut bersih sebelum build.

Gunakan vector (SVG atau CustomPainter) untuk ikon dan ilustrasi. Satu file SVG yang di-render via flutter_svg jauh lebih kecil dari setumpuk file PNG untuk berbagai density layar.


Langkah 6: Font Subsetting

Jika aplikasi Anda menggunakan custom font — terutama font Arab seperti Amiri atau Scheherazade — file font bisa sangat besar. Font Amiri lengkap, misalnya, berukuran sekitar 1.4 MB.

Jika Anda hanya menggunakan sebagian karakter, Anda bisa melakukan font subsetting menggunakan pyftsubset dari fonttools:

pip install fonttools brotli
pyftsubset Amiri-Regular.ttf
--unicodes="U+0600-U+06FF,U+FB50-U+FDFF,U+FE70-U+FEFF"
--layout-features="*"
--flavor=woff2
--output-file=Amiri-Arabic-subset.ttf

Range Unicode yang saya tentukan di atas mencakup blok Arabic, Arabic Presentation Forms-A, dan Arabic Presentation Forms-B — lebih dari cukup untuk menampilkan teks kitab kuning. Hasilnya font turun dari 1.4 MB menjadi sekitar 380 KB.


Langkah 7: ProGuard dan R8 untuk Native Code

Untuk mengurangi ukuran kode native dan Java/Kotlin yang digunakan oleh library Android, aktifkan ProGuard (atau penggantinya, R8) di konfigurasi build Gradle.

Di file android/app/build.gradle:

android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}

Flag shrinkResources true akan menghapus resource Android (layout, drawable, string) yang tidak direferensikan dari kode. Ini sering menghemat 100–500 KB tergantung seberapa banyak resource bawaan Flutter yang tidak Anda gunakan.

Satu catatan penting: setelah mengaktifkan ProGuard, selalu uji aplikasi secara menyeluruh. Beberapa library memerlukan aturan keep khusus di proguard-rules.pro agar tidak salah dioptimasi. Crash yang muncul hanya di build release (bukan debug) hampir selalu disebabkan oleh ProGuard yang terlalu agresif menghapus kelas yang masih dibutuhkan.


Hasil Akhir: Perbandingan Sebelum dan Sesudah

Berikut perbandingan ukuran APK setelah setiap tahap optimasi diterapkan secara berurutan:

  • Baseline (debug build): 52.3 MB
  • Setelah release build dasar: 24.8 MB
  • + Split per ABI (arm64 saja): 14.1 MB
  • + Obfuscation + split debug info: 11.6 MB
  • + Audit & hapus dependency besar: 9.3 MB
  • + Optimasi gambar ke WebP: 8.1 MB
  • + Font subsetting: 7.4 MB
  • + ProGuard + shrinkResources: 6.8 MB

Dari 24.8 MB ke 6.8 MB — pengurangan 72.6% — tanpa menghilangkan satu pun fitur dari aplikasi.


Bonus: App Bundle untuk Distribusi Play Store

Jika mendistribusikan melalui Google Play Store, pertimbangkan untuk menggunakan App Bundle (.aab) alih-alih APK:

flutter build appbundle --release 
--obfuscate
--split-debug-info=build/debug-info

Dengan App Bundle, Play Store yang menangani splitting per ABI, per density layar, dan per bahasa. Pengguna mendapatkan file yang paling minimal untuk perangkatnya. Rata-rata penghematan dibanding APK universal adalah 15–35%.

Satu batasan: App Bundle tidak bisa diinstall langsung dari file (sideloading). Jika Anda juga perlu mendistribusikan APK secara langsung (misalnya melalui website atau grup WhatsApp), Anda tetap perlu build APK terpisah.


Kesimpulan

Optimasi ukuran APK bukan satu langkah besar — ini akumulasi dari banyak keputusan kecil yang masing-masing memberikan kontribusi. Tidak ada satu teknik ajaib yang langsung memotong 70% ukuran; yang ada adalah disiplin untuk memeriksa setiap komponen dan mempertanyakan apakah itu benar-benar dibutuhkan.

Yang saya pelajari dari proses ini: banyak bloat di APK Flutter bukan karena Flutter-nya berat, tapi karena developer (termasuk saya di masa lalu) tidak memperhatikan apa yang dimasukkan ke dalam build. Setiap dependency memiliki biaya. Setiap gambar memiliki biaya. Setiap font memiliki biaya.

Ketika Anda mulai memperlakukan setiap kilobyte sebagai sesuatu yang perlu dipertanggungjawabkan, hasilnya adalah aplikasi yang tidak hanya lebih kecil — tapi lebih cepat, lebih bersih, dan lebih mudah dimaintain.

Dan pengguna Anda — terutama yang menggunakan HP lama dengan koneksi terbatas — akan merasakan perbedaannya.


Komentar

Belum ada komentar. Jadilah yang pertama menulis.

Tulis Komentar