Tampilan
Bab 38 — Branding & Rilis
Aplikasimu selesai. Sekarang bagian yang jarang diajarkan tutorial: mengubahnya dari proyek di komputermu menjadi aplikasi yang bisa dipasang orang lain.
Bab ini membahas identitas visual, penandatanganan, membangun berkas rilis, dan proses publikasi ke Play Store serta App Store.
Identitas aplikasi
Nama aplikasi
Nama yang muncul di bawah ikon berbeda dari nama proyek Flutter.
Android — android/app/src/main/AndroidManifest.xml:
xml
<application
android:label="Polling Kita"
android:icon="@mipmap/ic_launcher">iOS — ios/Runner/Info.plist:
xml
<key>CFBundleDisplayName</key>
<string>Polling Kita</string>
<key>CFBundleName</key>
<string>pollingkita</string>CFBundleDisplayName adalah yang terlihat pengguna; CFBundleName adalah nama internal yang tidak boleh mengandung spasi.
Application ID
Ini pengenal unik aplikasimu di seluruh dunia, dan tidak bisa diubah setelah dipublikasikan.
gradle
// android/app/build.gradle
android {
namespace "com.namaperusahaan.pollingkita"
defaultConfig {
applicationId "com.namaperusahaan.pollingkita"
minSdkVersion 23
targetSdkVersion 35
versionCode flutter.versionCode
versionName flutter.versionName
}
}Konvensinya adalah domain terbalik: kalau situsmu pollingkita.com, maka com.pollingkita.app.
Pilih dengan hati-hati
Application ID tidak bisa diubah setelah aplikasi terbit. Mengubahnya berarti membuat aplikasi baru — pengguna lama tidak akan mendapat pembaruan, dan ulasan serta peringkatmu hilang.
Hindari juga com.example.* — Play Store menolaknya.
Untuk iOS, ubah Bundle Identifier di Xcode: buka Runner.xcworkspace → Runner → Signing & Capabilities → Bundle Identifier.
Ikon aplikasi
Seperti dibahas di Bab 9, paket flutter_launcher_icons menghasilkan seluruh ukuran dari satu berkas.
yaml
# pubspec.yaml
dev_dependencies:
flutter_launcher_icons: ^0.14.1
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "assets/ikon/ikon_1024.png"
min_sdk_android: 23
# Ikon adaptif Android — latar dan depan terpisah
adaptive_icon_background: "#5E35B1"
adaptive_icon_foreground: "assets/ikon/ikon_depan.png"
# iOS tidak mendukung transparansi
remove_alpha_ios: truebash
dart run flutter_launcher_iconsPersyaratan gambarnya:
| Persyaratan | Alasan |
|---|---|
| PNG 1024×1024 | Ukuran terbesar yang dibutuhkan App Store |
| Persegi sempurna | Sistem yang membulatkan sudutnya |
| Tanpa transparansi (iOS) | iOS menampilkan latar hitam kalau transparan |
| Elemen penting di 66% tengah | Ikon adaptif Android bisa dipotong lingkaran |
Poin terakhir sering terlewat. Android memotong ikon adaptif menjadi berbagai bentuk tergantung peluncur — lingkaran, kotak membulat, atau bentuk lain. Kalau logomu memenuhi seluruh kanvas, bagian tepinya akan terpotong.
Kanvas ikon adaptif 108×108 dp
┌───────────────────────────────┐
│ ╭─────────────────────────╮ │
│ │ │ │
│ │ ╭───────────────╮ │ │
│ │ │ │ │ │
│ │ │ AMAN (66%) │ │ │ ← taruh logo di sini
│ │ │ │ │ │
│ │ ╰───────────────╯ │ │
│ │ bisa terpotong │ │
│ ╰─────────────────────────╯ │
└───────────────────────────────┘Splash screen
yaml
dev_dependencies:
flutter_native_splash: ^2.4.3
flutter_native_splash:
color: "#5E35B1"
image: assets/ikon/splash_logo.png
android_12:
image: assets/ikon/splash_logo_12.png
icon_background_color: "#5E35B1"
ios: true
android: truebash
dart run flutter_native_splash:createIni berbeda dari LayarSplash di Bab 28. Splash native muncul sebelum Flutter selesai dimuat; splash Flutter muncul sesudahnya. Keduanya sebaiknya terlihat sama agar transisinya mulus.
Versi
yaml
# pubspec.yaml
version: 1.2.0+15Formatnya versionName+versionCode:
1.2.0— yang dilihat pengguna. Ikuti semantic versioning:MAYOR.MINOR.PATCH.15— bilangan bulat yang harus selalu naik setiap unggahan.
1.2.0 + 15
│ │ │ │
│ │ │ └── versionCode: naik setiap unggahan, tanpa kecuali
│ │ └─────── PATCH: perbaikan bug
│ └───────── MINOR: fitur baru, kompatibel
└─────────── MAYOR: perubahan besarversionCode tidak boleh mundur
Play Store dan App Store menolak unggahan dengan versionCode yang sama atau lebih rendah dari yang sudah ada — bahkan kalau versi sebelumnya sudah dihapus.
Kalau kamu salah mengunggah dengan versionCode 100, kamu tidak bisa kembali ke 16 selamanya.
Membaca versi di dalam aplikasi:
dart
import 'package:package_info_plus/package_info_plus.dart';
Future<String> versiAplikasi() async {
final info = await PackageInfo.fromPlatform();
return '${info.version} (${info.buildNumber})';
}Berguna untuk ditampilkan di halaman "Tentang" — sangat membantu ketika pengguna melaporkan bug.
Penandatanganan Android
Setiap aplikasi Android harus ditandatangani. Kunci ini membuktikan bahwa pembaruan berasal dari kamu.
Membuat keystore
bash
keytool -genkey -v -keystore ~/kunci-rilis.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias rilisKamu akan diminta kata sandi dan beberapa informasi. Simpan semuanya dengan sangat baik.
Keystore hilang = aplikasi tidak bisa diperbarui
Kalau kamu kehilangan berkas .jks atau kata sandinya, kamu tidak bisa lagi menerbitkan pembaruan untuk aplikasi itu. Satu-satunya jalan adalah menerbitkan aplikasi baru dengan Application ID berbeda — kehilangan seluruh pengguna, ulasan, dan peringkat.
Simpan salinannya di tempat yang aman dan terpisah: pengelola kata sandi, penyimpanan awan terenkripsi, atau brankas fisik.
Play App Signing (opsi bawaan sekarang) mengurangi risiko ini karena Google menyimpan kunci penandatanganan sesungguhnya. Kamu tetap butuh upload key, tetapi itu bisa direset lewat dukungan Google kalau hilang. Aktifkan ini.
Menghubungkan ke proyek
properties
# android/key.properties — ⚠️ JANGAN di-commit
storePassword=kataSandiKeystore
keyPassword=kataSandiKunci
keyAlias=rilis
storeFile=/jalur/lengkap/ke/kunci-rilis.jksgradle
// android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile']
? file(keystoreProperties['storeFile'])
: null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
// Perkecil ukuran & samarkan kode
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}Aturan ProGuard untuk Flutter:
proguard
# android/app/proguard-rules.pro
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
# Kalau memakai Firebase
-keep class com.google.firebase.** { *; }
# Kalau memakai Stripe
-keep class com.stripe.android.** { *; }Tanpa aturan ini, minifyEnabled true bisa menghapus kelas yang dibutuhkan saat runtime — menghasilkan crash yang hanya terjadi di build rilis, jenis bug yang paling sulit dilacak.
Membangun berkas rilis
Android
bash
# App Bundle — WAJIB untuk Play Store
flutter build appbundle --release
# APK — untuk distribusi langsung atau pengujian
flutter build apk --release
# APK terpisah per arsitektur (ukuran lebih kecil)
flutter build apk --release --split-per-abiHasilnya:
build/app/outputs/bundle/release/app-release.aab ← untuk Play Store
build/app/outputs/flutter-apk/app-release.apk ← universal
build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk
build/app/outputs/flutter-apk/app-arm64-v8a-release.apkAAB versus APK
App Bundle (.aab) adalah format yang wajib untuk Play Store. Google menghasilkan APK yang dioptimalkan untuk setiap perangkat, sehingga unduhan pengguna 20–50% lebih kecil.
APK untuk distribusi di luar Play Store — situs web, pengujian internal, toko alternatif.
Dengan variabel lingkungan:
bash
flutter build appbundle --release \
--dart-define=STRIPE_PUBLISHABLE_KEY=pk_live_xxx \
--dart-define=MAPS_API_KEY=AIzaxxx \
--dart-define=ENV=productionUntuk banyak variabel, pakai berkas:
json
// env-produksi.json
{
"STRIPE_PUBLISHABLE_KEY": "pk_live_xxx",
"MAPS_API_KEY": "AIzaxxx",
"ENV": "production"
}bash
flutter build appbundle --release --dart-define-from-file=env-produksi.jsonJangan lupa menambahkan berkas itu ke .gitignore.
iOS
bash
flutter build ipa --releaseLalu unggah lewat Xcode atau:
bash
xcrun altool --upload-app \
--type ios \
--file build/ios/ipa/*.ipa \
--username "email@contoh.com" \
--password "kata-sandi-khusus-aplikasi"Memeriksa ukuran
bash
flutter build apk --release --analyze-sizeKeluarannya menunjukkan apa yang memakan ruang:
Total size: 18.4 MB
├── Dart AOT symbols 4.2 MB
├── assets/ 6.8 MB ← sering yang terbesar
├── lib/arm64-v8a/ 5.1 MB
└── res/ 2.3 MBCara mengecilkannya:
Perkecil gambar. Aset sering menjadi penyumbang terbesar. Kompres PNG dengan pngquant, atau ganti ke WebP:
bash
cwebp -q 80 gambar.png -o gambar.webpBuang paket yang tidak dipakai.
bash
flutter pub deps --style=compactPakai --split-per-abi kalau mendistribusikan APK.
Aktifkan minifikasi seperti pada konfigurasi Gradle di atas.
Menguji build rilis
Ini langkah yang paling sering dilewati dan paling sering menyelamatkan.
bash
flutter run --releaseBeberapa masalah hanya muncul di build rilis:
- Kelas yang terhapus oleh ProGuard.
- Kode yang bergantung pada
assert— yang dihapus di rilis. - Perbedaan performa yang nyata.
- Kunci API produksi yang salah.
- Handler notifikasi latar belakang yang terhapus (lihat Bab 30).
Daftar periksa sebelum mengunggah:
- Diuji di perangkat FISIK, bukan hanya emulator
- Diuji dalam mode --release
- Semua kunci API sudah versi produksi
- Login, pembayaran, dan notifikasi berfungsi
- Tidak ada print() atau data uji yang tertinggal
- Mode gelap dan terang keduanya benar
- Perilaku offline sudah diperiksa
- Diuji di layar kecil DAN tablet
- Kebijakan privasi tersedia dan tautannya benar
Google Play Store
Persiapan
Akun pengembang: biaya sekali seumur hidup US$25 di play.google.com/console.
Yang perlu disiapkan:
| Aset | Spesifikasi |
|---|---|
| Ikon | 512×512 PNG, tanpa transparansi |
| Gambar unggulan | 1024×500 JPG/PNG |
| Tangkapan layar ponsel | Minimal 2, maksimal 8 |
| Tangkapan layar tablet | Opsional tetapi disarankan |
| Deskripsi singkat | Maksimal 80 karakter |
| Deskripsi lengkap | Maksimal 4000 karakter |
| Kebijakan privasi | URL yang bisa diakses publik |
Jalur rilis
Internal testing → Closed testing → Open testing → Production
(100 penguji) (grup terpilih) (siapa saja) (semua orang)
Tersedia menit Butuh peninjauan Butuh peninjauanAlur yang disarankan: mulai dari internal testing untuk memastikan build-nya benar-benar berfungsi, lalu naik bertahap.
Persyaratan pengujian untuk akun baru
Akun pengembang perorangan yang dibuat setelah November 2023 wajib menjalankan closed testing dengan minimal 12 penguji selama 14 hari berturut-turut sebelum boleh menerbitkan ke produksi.
Rencanakan ini sejak awal — mencari 12 orang yang bersedia memasang aplikasimu selama dua minggu butuh waktu.
Data safety
Play Store mewajibkan deklarasi data apa yang kamu kumpulkan. Jawab dengan jujur — pemeriksaan otomatis Google membandingkan deklarasimu dengan izin dan pustaka yang dipakai aplikasi.
Untuk aplikasi seperti yang kita bangun:
- Informasi pribadi — Nama, alamat email (untuk akun)
- Lokasi — Lokasi perkiraan (untuk polling terdekat)
- Foto & video — Foto (untuk profil dan polling)
- Pesan — Pesan dalam aplikasi (chatbot)
- Informasi aplikasi & performa — Log crash (Crashlytics)
Apple App Store
Persiapan
Akun pengembang: US$99 per tahun di developer.apple.com. Kamu juga butuh Mac.
| Aset | Spesifikasi |
|---|---|
| Ikon | 1024×1024 PNG, tanpa transparansi, tanpa sudut membulat |
| Tangkapan layar iPhone 6.7" | Wajib |
| Tangkapan layar iPad 12.9" | Wajib kalau mendukung iPad |
| Deskripsi | Maksimal 4000 karakter |
| Kata kunci | Maksimal 100 karakter, dipisah koma |
| Kebijakan privasi | URL wajib |
Peninjauan Apple
Apple meninjau setiap aplikasi secara manual. Penolakan yang paling umum:
Guideline 2.1 — informasi tidak lengkap. Sediakan akun demo dengan data yang sudah terisi. Kalau peninjau tidak bisa masuk, aplikasimu ditolak.
Guideline 3.1.1 — pembayaran di luar sistem Apple. Konten digital wajib memakai In-App Purchase. Lihat peringatan di Bab 36.
Guideline 4.2 — fungsi minimal. Aplikasi yang hanya membungkus situs web akan ditolak.
Guideline 5.1.1 — privasi. Setiap izin harus punya penjelasan yang jelas dan spesifik di Info.plist.
Isi catatan peninjau dengan baik
Akun demo:
Email: reviewer@contoh.com
Kata sandi: Demo1234!
Catatan:
- Fitur "Polling Terdekat" membutuhkan izin lokasi. Akun demo
sudah punya beberapa polling di sekitar Cupertino.
- Fitur langganan memakai sandbox; kartu uji tidak diperlukan.
- Chatbot AI membutuhkan koneksi internet.Peninjau yang bisa langsung mencoba aplikasimu jauh lebih mungkin menyetujuinya pada percobaan pertama.
Crashlytics
Memasangnya sebelum rilis jauh lebih baik daripada sesudah kamu mendapat laporan "aplikasinya crash" tanpa detail.
yaml
dependencies:
firebase_crashlytics: ^4.2.0dart
// lib/main.dart
import 'dart:async';
import 'dart:ui';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// Jangan kirim laporan saat pengembangan
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(!kDebugMode);
// Tangkap error Flutter
FlutterError.onError = (rincian) {
FlutterError.presentError(rincian);
FirebaseCrashlytics.instance.recordFlutterFatalError(rincian);
};
// Tangkap error di luar Flutter (isolate, callback native)
PlatformDispatcher.instance.onError = (kesalahan, jejak) {
FirebaseCrashlytics.instance.recordError(kesalahan, jejak, fatal: true);
return true;
};
runApp(const AplikasiSaya());
}Menambahkan konteks membuat laporan jauh lebih berguna:
dart
// Identifikasi pengguna (jangan pakai data pribadi)
await FirebaseCrashlytics.instance.setUserIdentifier(uid);
// Kunci khusus yang muncul di laporan
await FirebaseCrashlytics.instance.setCustomKey('tingkat', 'pro');
await FirebaseCrashlytics.instance.setCustomKey('layar', 'detail_polling');
// Jejak langkah sebelum crash
FirebaseCrashlytics.instance.log('Pengguna membuka polling abc123');
// Catat error yang tidak fatal
try {
await repository.muat();
} catch (e, jejak) {
await FirebaseCrashlytics.instance.recordError(e, jejak, fatal: false);
}Otomatisasi rilis
Setelah beberapa kali merilis manual, otomatisasi menjadi sepadan.
yaml
# .github/workflows/rilis.yml
name: Rilis
on:
push:
tags: ['v*']
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.x'
channel: stable
cache: true
- name: Pulihkan keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/rilis.jks
cat > android/key.properties <<EOF
storePassword=${{ secrets.STORE_PASSWORD }}
keyPassword=${{ secrets.KEY_PASSWORD }}
keyAlias=${{ secrets.KEY_ALIAS }}
storeFile=rilis.jks
EOF
- name: Pulihkan konfigurasi Firebase
run: echo "${{ secrets.GOOGLE_SERVICES_JSON }}" > android/app/google-services.json
- run: flutter pub get
- name: Bangun App Bundle
run: |
flutter build appbundle --release \
--dart-define=STRIPE_PUBLISHABLE_KEY=${{ secrets.STRIPE_PK }} \
--dart-define=MAPS_API_KEY=${{ secrets.MAPS_KEY }}
- name: Unggah ke Play Store (jalur internal)
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
packageName: com.namaperusahaan.pollingkita
releaseFiles: build/app/outputs/bundle/release/app-release.aab
track: internal
status: completedSekarang merilis cukup dengan:
bash
git tag -a v1.2.0 -m "Rilis 1.2.0"
git push origin v1.2.0README untuk proyek
Bukan bagian dari rilis ke toko, tetapi penting kalau proyekmu di GitHub.
markdown
# Polling Kita
Aplikasi jajak pendapat dengan lokasi, dibangun dengan Flutter & Firebase.


## Fitur
- Buat dan bagikan jajak pendapat
- Temukan polling di sekitarmu
- Asisten AI untuk membantu membuat polling
- Bekerja tanpa koneksi internet
- Notifikasi saat ada suara baru
## Tangkapan Layar
| Beranda | Detail | Peta |
| --- | --- | --- |
|  |  |  |
## Menjalankan Secara Lokal
```bash
git clone https://github.com/pengguna/polling-kita.git
cd polling-kita
flutter pub get
# Salin dan isi konfigurasi
cp env.example.json env.json
flutter run --dart-define-from-file=env.json
```
## Arsitektur
Lihat [ARSITEKTUR.md](docs/ARSITEKTUR.md).
## Lisensi
MIT — lihat [LICENSE](LICENSE).Latihan Mandiri
Kerjakan salah satu, beberapa, atau semuanya secara berurutan untuk melatih pemahamanmu sampai benar-benar lekat.
Variasi 1: Identitas dan Build Rilis — ⭐ · 30–45 menit
Tantangan: Beri identitas lengkap pada satu aplikasi yang sudah kamu punya: nama, application ID, ikon peluncur, dan splash. Lalu bangun berkas rilisnya dan jalankan di perangkat fisik. Berhenti sebelum mengunggah ke toko — itu bukan bagian dari latihan ini.
Bayangan tampilan (kenapa aturan 66% penting):
text
Ikon sumber 1024×1024 Setelah dipotong jadi bentuk adaptif:
┌─────────────────┐ ⬤ ▢ 💧
│ ░░░░░░░░░░░░░░░ │ LOGO LOGO LOGO
│ ░░┌───────────┐░ │ (bulat) (kotak) (tetes)
│ ░░│ LOGO │░ │ → utuh di ketiganya, karena logo
│ ░░│ (66% aman)│░│ ada di area 66% tengah
│ ░░└───────────┘░ │
│ ░░░░░░░░░░░░░░░ │
└─────────────────┘
Logo yang SALAH (memenuhi seluruh kanvas):
⬤
LO ← "GO" terpotong hilang, karena logo
menyentuh tepi kanvas, bukan cuma 66% tengahKriteria selesai:
- Application ID bukan
com.example.*, dan kamu memahami bahwa ia tidak bisa diubah setelah aplikasi terbit. - Ikon dibuat dari satu PNG 1024×1024 persegi tanpa transparansi, dengan elemen penting berada di 66% area tengah.
- Ikon terlihat benar di Android maupun iOS — periksa langsung di layar beranda perangkat, bukan hanya di berkas hasil.
flutter run --releaseberjalan di perangkat fisik tanpa masalah yang tidak muncul di mode debug.--analyze-sizedijalankan sekali, dan kamu tahu tiga hal terbesar yang memakan ruang di aplikasimu.
Petunjuk: Aturan 66% ada karena ikon adaptif Android dipotong menjadi berbagai bentuk oleh peluncur — lingkaran, kotak bulat, tetesan air — dan tiap pabrikan memilih sendiri. Logo yang memenuhi seluruh kanvas akan terpotong di sebagian perangkat. Untuk build rilis yang bermasalah padahal debug baik-baik saja, tersangka pertama adalah minifyEnabled true tanpa aturan ProGuard yang memadai: kode yang dipanggil lewat refleksi dibuang karena terlihat tidak terpakai. Dan sebelum membuat keystore, baca sekali lagi bagian tentang kehilangan keystore. Itu satu dari sedikit kesalahan di seluruh situs ini yang benar-benar tidak bisa dipulihkan.
Variasi 2: Penandatanganan yang Aman — ⭐⭐ · 40–60 menit
Tantangan: Siapkan penandatanganan rilis Android dengan benar: buat keystore, konfigurasikan build, dan susun prosedur pencadangan yang benar-benar akan kamu jalankan. Lalu buktikan bahwa berkas rahasianya tidak akan pernah ikut ter-commit.
Kriteria selesai:
key.propertiesdan berkas keystore ada di.gitignore, dangit statusmembuktikan keduanya tidak terlihat.- Build rilis benar-benar tertandatangani, dan kamu bisa memverifikasinya.
- Ada prosedur pencadangan tertulis dengan minimal dua salinan di tempat berbeda.
- Kamu memahami apa yang dilakukan Play App Signing dan memutuskan apakah akan memakainya, disertai alasan.
Petunjuk: Kehilangan keystore berarti kamu tidak akan pernah bisa memperbarui aplikasimu lagi — pengguna harus memasang ulang aplikasi baru dengan identitas berbeda, dan seluruh ulasan serta pemasangan hilang. Play App Signing mengurangi risiko itu dengan menyimpan kunci penandatangan akhir di sisi Google, sehingga yang kamu pegang hanya kunci unggah yang bisa diganti kalau hilang. Untuk sebagian besar orang, itu pilihan yang jelas.
Variasi 3: Ukuran dan Kesehatan Rilis — ⭐⭐⭐ · 45–60 menit
Tantangan: Kecilkan ukuran aplikasimu dan pasang pelaporan crash. Ukur ukuran sebelum dan sesudah, temukan tiga hal terbesar yang memakan ruang, dan buktikan pelaporan crash bekerja dengan memicu crash yang disengaja di build rilis.
Kriteria selesai:
- Ukuran sebelum dan sesudah tercatat dalam angka, dan kamu bisa menyebut apa yang memberi pengurangan terbesar.
- App Bundle dipakai, bukan APK universal, dan kamu tahu selisih ukurannya.
- Crash yang disengaja muncul di konsol pelaporan lengkap dengan jejak tumpukan yang terbaca.
- Jejak tumpukan build rilis tidak berupa kode teracak — berkas simbolnya sudah diunggah.
Petunjuk: Aset hampir selalu menjadi penyumbang terbesar, dan gambar yang tidak diperkecil adalah tersangka pertama. Kriteria terakhir adalah yang paling sering baru disadari saat dibutuhkan: build rilis yang diperkecil menghasilkan jejak tumpukan yang tidak terbaca kecuali berkas pemetaan simbolnya diunggah ke layanan pelaporan. Melakukannya sekarang jauh lebih murah daripada saat kamu sedang panik mengejar crash yang hanya terjadi di perangkat pengguna.
Ikhtisar
- Application ID tidak bisa diubah setelah aplikasi terbit. Pilih dengan hati-hati, hindari
com.example.*. - Ikon: PNG 1024×1024, persegi, tanpa transparansi, dengan elemen penting di 66% area tengah karena ikon adaptif Android bisa dipotong.
versionCode(angka setelah+) harus selalu naik dan tidak pernah bisa mundur.- Kehilangan keystore berarti aplikasi tidak bisa diperbarui selamanya. Aktifkan Play App Signing untuk mengurangi risiko ini.
key.propertiesdan*.jkstidak boleh di-commit — lihat Bab 37.minifyEnabled truebutuh aturan ProGuard, atau muncul crash yang hanya terjadi di build rilis.- App Bundle (
.aab) wajib untuk Play Store dan menghasilkan unduhan 20–50% lebih kecil daripada APK universal. - Selalu uji
flutter run --releasedi perangkat fisik sebelum mengunggah. Banyak masalah hanya muncul di sana. --analyze-sizemenunjukkan apa yang memakan ruang; aset biasanya yang terbesar.- Akun Play Store perorangan baru wajib closed testing 12 penguji selama 14 hari sebelum bisa ke produksi.
- Apple meninjau manual. Sediakan akun demo dan catatan peninjau yang jelas — ini yang paling menentukan disetujui pada percobaan pertama.
- Pasang Crashlytics sebelum rilis, dengan
setCustomKeydanlogagar laporannya berguna. - Otomatiskan rilis dengan GitHub Actions yang terpicu oleh tag.
Berikutnya: Bab 39 — ASO (App Store Optimization).
Transkrip asli
Disintesis dari 4_flutter_ai-chatbot-n-firebase/11_project-branding-and-documentation.md (video 1–2: Creating a Professional README, Generating Custom App Icons), 6_firebase-cloud-messaging-and-apk-generation.md (video 3: Configure Flutter App Icons and Build Android & iOS Apps), dan 3_flutter-monetize/20_publishing-in-multiple-app-stores.md. Lihat PDF Firebase & AI dan PDF Monetize.