Ukuran unduhan aplikasi (download size) berpengaruh langsung terhadap kesuksesan bisnis digital. Studi Google Play berjudul Shrinking APKs, Growing Installs (2017) menemukan bahwa untuk aplikasi di bawah 100 MB, setiap kenaikan ukuran APK sebesar 6 MB berkorelasi dengan penurunan konversi instalasi sekitar 1%, dengan dampak yang lebih besar di pasar berkembang. Datanya memang sudah lama, tetapi logikanya masih berlaku: pengguna dengan kuota terbatas atau memori penyimpanan mepet akan berpikir dua kali sebelum mengunduh aplikasi yang besar.
Selain ukuran berkas, performa render yang tersendat (frame drops atau jank) saat pengguna melakukan scrolling adalah pemicu utama ulasan bintang 1 dan pencopotan pemasangan (uninstall). Panduan ini membahas cara mengukur, memangkas ukuran aplikasi Flutter di Android maupun iOS, dan menjaga render tetap mulus di 60–120 FPS.
- Ukur dulu dengan flutter build --analyze-size dan DevTools. Ukuran upload package bukan ukuran unduhan pengguna yang sebenarnya.
- Rilis Android dalam format AAB (wajib untuk aplikasi baru di Google Play). Gunakan --split-per-abi bila harus membagikan APK di luar Play Store.
- --split-debug-info memangkas ukuran biner, sedangkan --obfuscate menambah proteksi kode tetapi bukan jaminan keamanan. Simpan folder symbols setiap rilis agar crash log tetap bisa dibaca.
- Aktifkan R8 (minify dan shrink resources) untuk layer Android, kompres aset besar, dan subset font teks kustom secara manual. Icon font sudah di-tree-shake otomatis oleh Flutter.
- Pindahkan parsing JSON besar ke isolate (Isolate.run atau compute()) dan lakukan profiling di profile mode pada perangkat fisik.
1. Ukur Dulu Sebelum Mengoptimasi Ukuran Aplikasi
Jangan mengukur dari build debug. Build debug membawa overhead hot reload dan debugging sehingga jauh lebih besar dari aplikasi yang diunduh pengguna. Hasil flutter build apk atau flutter build ios standar pun hanyalah upload package: Play Store dan App Store akan memprosesnya ulang dan memecahnya sesuai arsitektur CPU serta densitas layar tiap perangkat, sehingga ukuran berkas yang terlihat tidak sama dengan ukuran unduhan pengguna.
Bedah Isi Aplikasi dengan --analyze-size
Tambahkan flag --analyze-size saat build. Flutter menampilkan ringkasan ukuran per kategori (aset, kode native, pustaka Flutter, dan paket Dart) di terminal, sekaligus menyimpan berkas *-code-size-analysis_*.json yang bisa dibuka di DevTools sampai level fungsi. Anda juga bisa memuat dua berkas JSON dari dua build berbeda untuk membandingkan dampak tiap perubahan.
flutter build appbundle --analyze-size # Buka DevTools, pilih Open app size tool, lalu unggah berkas *-code-size-analysis_*.jsondart devtools
Cek Ukuran Unduhan Sebenarnya di Android
Unggah berkas .aab ke Google Play Console, lalu buka Android vitals → App size. Ukuran unduhan di sana dihitung untuk perangkat XXXHDPI dengan arsitektur arm64-v8a, jadi angka di perangkat pengguna lain bisa sedikit berbeda. Untuk iOS, caranya dibahas di bagian iOS di bawah.
2. Memangkas Ukuran Aplikasi Android
A. Gunakan Android App Bundle (AAB)
Jangan merilis berkas APK universal (fat APK) yang menggabungkan seluruh arsitektur CPU (armeabi-v7a, arm64-v8a, x86_64) ke pengguna Google Play. Google Play sudah mewajibkan format AAB untuk aplikasi baru sejak Agustus 2021. Dengan AAB, Play otomatis memecah paket sehingga pengguna hanya mengunduh binary, aset, dan bahasa yang cocok dengan perangkat mereka.
B. Distribusi di Luar Play Store: Gunakan --split-per-abi
Jika harus membagikan APK langsung ke pengguna atau ke toko aplikasi alternatif, jangan bagikan fat APK. Jalankan flutter build apk --split-per-abi untuk menghasilkan satu APK per arsitektur, lalu bagikan yang sesuai. Dengan begitu pengguna tidak perlu membawa binary arsitektur yang tidak dipakai perangkatnya.
C. Pisahkan Simbol Debug dengan --split-debug-info
Saat membuat build rilis, tambahkan --split-debug-info=./build_symbols. Flag ini memindahkan tabel simbol debug keluar dari biner utama ke folder terpisah sehingga ukuran biner turun. Dokumentasi resmi Flutter menyebut flag ini dapat memangkas ukuran kode secara dramatis.
D. Obfuscation dengan --obfuscate
--obfuscate adalah flag terpisah yang mengganti nama simbol Dart menjadi nama pendek tak bermakna. Ini mempersulit reverse engineering, tetapi bukan jaminan keamanan, dan tujuan utamanya bukan memperkecil ukuran. Flag ini wajib dipasangkan dengan --split-debug-info.
Simpan Folder Symbols Setiap Rilis
E. Aktifkan R8 (Minify dan Shrink Resources)
R8 bekerja pada layer Android (kode Java/Kotlin dari embedding Flutter dan plugin), bukan pada kode Dart. Kode Dart sudah di-tree-shake oleh compiler AOT pada build profile dan release. Aktifkan minify dan shrink resources pada build type release. Sesuaikan dengan DSL proyek Anda: pada Groovy namanya minifyEnabled dan shrinkResources.
1android {2 buildTypes {3 release {4 isMinifyEnabled = true5 isShrinkResources = true6 proguardFiles(7 getDefaultProguardFile("proguard-android-optimize.txt"),8 "proguard-rules.pro"9 )10 }11 }12}
Uji build rilis setelah mengaktifkannya. R8 dapat menghapus kelas yang dipakai lewat refleksi, sehingga plugin tertentu memunculkan error Missing classes saat build atau crash saat runtime. Solusinya biasanya menambahkan aturan keep di proguard-rules.pro.
Penyusutan Ukuran Aplikasi
Ukuran unduhan pengguna berkurang dari 46 MB menjadi hanya 18.5 MB di perangkat konsumen.
3. Memangkas Ukuran Aplikasi iOS
Sama seperti Android, ukuran IPA bukan ukuran unduhan pengguna: App Store menerapkan app thinning sehingga tiap perangkat hanya menerima varian yang dibutuhkan. Dokumentasi Flutter juga mencatat bahwa IPA umumnya lebih besar dari APK. Flag --split-debug-info dan optimasi aset dari bagian lain tetap berlaku di iOS. Untuk memperkirakan ukuran nyata, buat App Size Report dari Xcode:
Jalankan
flutter build ipa --export-method development.Buka archive dengan
open build/ios/archive/*.xcarchive.Klik Distribute App, lalu pilih metode distribusi Development.
Pada App Thinning, pilih all compatible device variants dan aktifkan Strip Swift symbols.
Setelah export, buka berkas
App Thinning Size Report.txtuntuk melihat proyeksi ukuran per perangkat dan versi iOS.
--analyze-size di iOS Menghasilkan .app, Bukan IPA
4. Optimasi Aset, Font, dan Dependency
Hapus aset dan paket yang tidak terpakai: tinjau
pubspec.yamlsecara berkala. Ringkasan--analyze-sizemenunjukkan paket Dart mana yang paling besar.Kompres gambar raster: kompres PNG dan JPEG, dan pertimbangkan WebP untuk aset besar.
Perhatikan font: Flutter otomatis men-tree-shake icon font (misalnya Material Icons) pada build rilis, tetapi font teks kustom tidak di-subset otomatis. Sertakan hanya weight yang benar-benar dipakai dan pertimbangkan subsetting manual, misalnya dengan fonttools.
Ganti raster dengan vektor bila memungkinkan: lihat evaluasi di bawah.
Evaluasi Aset: Gambar Bitmap (PNG/JPG) vs Grafis Vektor (SVG/Lottie)
- Ukuran berkas dalam hitungan kilobyte (sangat hemat memori)
- Kualitas visual tetap sangat tajam di resolusi layar smartphone apapun (zero blur)
- Animasi kompleks interaktif tanpa perlu mengekspor deretan frame video besar
- Rendering grafis SVG yang memiliki ribuan path node rumit dapat membebani GPU perangkat berspesifikasi rendah
Ringkasan Teknik dan Dampak Relatifnya
Dampak di bawah adalah estimasi relatif, bukan angka pasti. Selalu ukur di proyek Anda dengan --analyze-size.
| Teknik | Platform | Dampak relatif | Catatan |
|---|---|---|---|
| AAB (bukan APK universal) | Android | Tinggi | Wajib untuk aplikasi baru di Google Play |
| --split-per-abi | Android (APK di luar Play) | Tinggi | Tidak diperlukan jika distribusi lewat AAB |
| --split-debug-info | Android dan iOS | Sedang sampai tinggi | Simpan folder symbols setiap rilis |
| R8 (minify + shrink resources) | Android | Sedang | Uji build rilis, mungkin butuh aturan keep |
| Kompres aset, WebP, hapus aset tak terpakai | Android dan iOS | Tergantung proyek | Cek kontribusi aset lewat --analyze-size |
| Subset font teks kustom | Android dan iOS | Tergantung proyek | Icon font sudah otomatis di-tree-shake |
| --obfuscate | Android dan iOS | Kecil untuk ukuran | Tujuannya proteksi kode, bukan ukuran |
5. Mengeliminasi Jank Render untuk Animasi Mulus 60/120 FPS
Setiap frame punya anggaran waktu (frame budget): sekitar 16,6 milidetik untuk layar 60Hz dan sekitar 8,3 milidetik untuk layar 120Hz. Jika penyajian sebuah frame melewati anggaran itu, frame terlewat dan pengguna melihatnya sebagai gerakan yang tersendat.
Lakukan Profiling di Profile Mode
Jalankan flutter run --profile pada perangkat fisik, idealnya kelas menengah ke bawah yang mewakili pengguna Anda, lalu buka tab Performance di DevTools. Dari sana terlihat frame mana yang melebihi anggaran, baik di UI thread maupun raster thread. Mode debug tidak representatif untuk mengukur performa.
Penyebab Jank yang Paling Umum
Kompilasi shader: mesin grafis Impeller meng-precompile shader sehingga menghilangkan shader compilation jank yang dulu umum pada Skia. Impeller aktif secara default di iOS dan di Android API 29 ke atas. Perangkat Android lama masih dapat memakai Skia, jadi uji juga di perangkat entry-level dan periksa catatan rilis versi Flutter yang Anda pakai karena statusnya terus berkembang.
Pekerjaan berat di main isolate: parsing respons JSON berisi ribuan item di isolate utama menyita CPU selama ratusan milidetik dan membuat animasi berhenti seketika.
Rebuild widget berlebihan: gunakan
constconstructor,ListView.builderuntuk daftar panjang, dan batasi area widget yang dibangun ulang.
Pindahkan Pekerjaan Berat ke Isolate
6. Contoh Kode: Parsing JSON di Isolate dan Perintah Build Produksi
Berikut contoh memindahkan parsing payload besar ke isolate latar belakang dengan Isolate.run, cara paling sederhana menurut dokumentasi Flutter. Jika aplikasi Anda juga menargetkan web, gunakan compute() dari package:flutter/foundation.dart agar kode tetap bisa dikompilasi.
1import "dart:convert";2import "dart:isolate";3 4// Fungsi murni untuk mem-parsing data transaksi5List<TransactionModel> _decodeTransactions(String rawResponseBody) {6 final jsonList = jsonDecode(rawResponseBody) as List<dynamic>;7 return jsonList8 .map((e) => TransactionModel.fromJson(e as Map<String, dynamic>))9 .toList();10}11 12// Menjalankan parsing di isolate terpisah agar UI tetap mulus13Future<List<TransactionModel>> parseLargeTransactionPayload(String rawBody) {14 return Isolate.run(() => _decodeTransactions(rawBody));15}
Dan berikut perintah build rilis yang menggabungkan teknik di atas, plus cara membaca stack trace dari build yang di-obfuscate:
1# Android (Google Play)2flutter build appbundle --release --obfuscate --split-debug-info=./build_symbols/android3 4# Android (APK untuk distribusi di luar Play Store)5flutter build apk --release --split-per-abi --split-debug-info=./build_symbols/android6 7# iOS8flutter build ipa --release --obfuscate --split-debug-info=./build_symbols/ios9 10# Membaca stack trace dari build yang di-obfuscate11flutter symbolize --input=stacktrace.txt -d ./build_symbols/android
Hubungan Antara Performa Aplikasi dan Arsitektur
Aplikasi yang ringan dan responsif harus didukung oleh manajemen state yang efisien agar tidak terjadi render ulang (unnecessary rebuilds) pada seluruh pohon widget. Pelajari cara mengisolasi pembangunan ulang widget secara presisi melalui panduan Tutorial Manajemen State Flutter BLoC dan tentukan perbandingan teknologi di artikel Flutter vs Native vs React Native.
Aplikasi Mobile Perusahaan Anda Lambat atau Berukuran Terlalu Besar?
Diskusikan audit performa aplikasi, optimasi framerate 60 FPS, dan perampingan ukuran biner bersama engineer mobile Arkatek.
Pertanyaan Umum Seputar Optimasi Ukuran Aplikasi Flutter
Kenapa ukuran aplikasi Flutter lebih besar dari aplikasi native?
Aplikasi Flutter bersifat self-contained: paket instalasinya membawa engine, runtime, kode, dan aset sendiri. Ukuran dasar itu tidak bisa dihilangkan sepenuhnya, tetapi bisa ditekan dengan teknik di artikel ini. Untuk membandingkan konsekuensi tiap teknologi, baca Flutter vs Native vs React Native.
Apa beda ukuran berkas upload (AAB/IPA) dengan ukuran unduhan pengguna?
Google Play dan App Store memproses ulang paket yang Anda unggah, lalu memfilter arsitektur CPU dan densitas aset sesuai perangkat pengguna. Karena itu ukuran unduhan sebenarnya harus dicek lewat Play Console (Android vitals) dan App Thinning Size Report atau App Store Connect (iOS).
Apakah --obfuscate memperkecil ukuran aplikasi?
Bukan itu tujuan utamanya. Pemangkasan ukuran datang dari --split-debug-info, sedangkan --obfuscate berfungsi mempersulit reverse engineering dan bukan jaminan keamanan. Keduanya dipakai bersamaan, dan folder symbols wajib disimpan.
Lebih baik merilis AAB atau APK?
Untuk Google Play, gunakan AAB, karena format ini wajib untuk aplikasi baru sejak Agustus 2021. APK tetap relevan untuk distribusi manual atau toko alternatif; pada kasus itu gunakan --split-per-abi agar tidak membagikan fat APK.
Apakah Impeller menghilangkan jank sepenuhnya?
Impeller menghilangkan sumber shader compilation jank, tetapi tidak menyelesaikan pekerjaan berat di main isolate atau rebuild widget yang berlebihan. Profiling di profile mode tetap diperlukan.
Kesimpulan
Mengoptimalkan ukuran dan performa aplikasi mobile bukanlah sentuhan akhir yang dikerjakan terburu-buru sebelum rilis, melainkan disiplin rekayasa yang harus diterapkan sejak awal. Mulailah dengan mengukur menggunakan --analyze-size, pangkas ukuran lewat AAB, --split-debug-info, R8, dan optimasi aset, lalu pastikan render tetap mulus dengan profiling dan pemindahan pekerjaan berat ke isolate. Hasilnya adalah aplikasi yang lebih cepat diunduh dan lebih nyaman dipakai.
Referensi resmi: Measuring your app's size dan Concurrency and isolates di dokumentasi Flutter.