Lompat ke konten
Semua studi kasus

Studi kasus rekayasa

Membangun Kemampuan Screen Time di Flutter

Pemantauan penggunaan dan pembatasan aplikasi Android, dengan jembatan izin iOS — studi kasus plugin Flutter yang memperjelas batas dukungan platform.

Dart ↔ Native
Flutter API
AndroidUsageStats
iOSPermissions
Catatan engineering / diagram konsep
Peran
Penulis & Pemelihara Plugin
Perusahaan
Solusi Bejo
Periode
April 2025 — Sekarang
Diperbarui
7 September 2026
  • Flutter
  • Dart
  • Kotlin
  • Swift
  • Android
  • iOS
  • Platform Channels

Screen Time adalah plugin Flutter untuk membangun aplikasi yang membantu orang mengelola penggunaan perangkat Android: membaca statistik pemakaian, memantau aplikasi aktif, serta menerapkan atau menjadwalkan pembatasan aplikasi. Implementasi iOS terbatas pada permintaan izin dan pemeriksaan status otorisasi.

Ini contoh paling jelas dari apa yang terjadi ketika kebutuhan produk jatuh sepenuhnya di luar framework. Tidak ada API Dart untuk satu pun hal di atas, dan kedua platform bahkan tidak sepakat soal apa arti "screen time".

Lihat handler metode Android dan handler metode iOS untuk implementasi yang ditinjau.

Situasinya

Kebutuhannya sederhana untuk dinyatakan: biarkan pengguna melihat ke mana waktunya pergi, dan biarkan mereka membatasinya.

Setiap bagian dari kalimat itu adalah kemampuan sistem operasi yang dijaga izin istimewa — karena aplikasi yang bisa melihat aplikasi lain yang Anda pakai, dan menghentikan Anda memakainya, persis berbentuk seperti aplikasi yang rawan disalahgunakan.

Masalahnya

Android dan iOS memilih model yang berlawanan.

Android menyediakan bahan mentahnya dan menyuruh kita merakit sendiri. UsageStatsManager memberi data pemakaian historis, tapi hanya setelah pengguna memberikan PACKAGE_USAGE_STATS — izin yang tidak bisa diminta lewat dialog runtime biasa, dan diperiksa lewat AppOpsManager alih-alih API izin standar. Deteksi aplikasi aktif secara real time sama sekali tidak tersedia lewat jalur itu; butuh AccessibilityService, yang harus diaktifkan pengguna secara manual di pengaturan sistem. Memblokir aplikasi berarti menggambar di atasnya, yang butuh SYSTEM_ALERT_WINDOW.

Kode iOS memakai FamilyControls.AuthorizationCenter untuk otorisasi. Jembatan izin ini belum mengimplementasikan laporan penggunaan atau pembatasan aplikasi. Fitur-fitur itu membutuhkan pekerjaan native tambahan; otorisasi saja belum menyediakannya.

Batas implementasi ini penting bagi pemakai plugin: antarmuka Dart yang sama belum berarti Android dan iOS menyediakan kemampuan yang sama.

Batasannya

Tiga batasan membentuk setiap keputusan:

Izin tidak bisa diminta secara normal. PACKAGE_USAGE_STATS, aksesibilitas, dan overlay masing-masing menuntut kita mengirim pengguna ke layar pengaturan sistem yang berbeda, lalu mendeteksi saat mereka kembali apakah izinnya benar-benar diberikan.

Layanan aksesibilitas sensitif secara kebijakan. Memakai API aksesibilitas untuk hal selain aksesibilitas mengundang pemeriksaan ketat dari Play Store. Ini risiko distribusi yang nyata, bukan teoretis.

Pekerjaan latar harus bertahan dari sistem operasi. Fitur pemblokiran yang berhenti bekerja setelah perangkat di-reboot, atau saat prosesnya dimatikan sistem, lebih buruk daripada tidak ada fitur sama sekali — pengguna merasa terlindungi padahal tidak.

Pendekatan Rekayasa

Plugin ini dibangun dalam potongan kecil yang bisa dirilis, selama kurang lebih delapan bulan, dan riwayat versinya adalah catatan jujur tentang bagaimana pemahamannya berkembang:

0.1.0  Mengambil daftar aplikasi terpasang
0.2.0  UsageStatsManager · pemeriksaan izin AppOpsManager · AccessibilityService
0.3.0  Menyaring aplikasi yang dipasang pengguna; pemakaian per paket
0.4.0  Permintaan izin dan status izin yang tersentralisasi
0.5.0  Izin draw overlay
0.6.0  Pemblokiran aplikasi
0.7.0  Blokir dan buka blokir aplikasi tertentu
0.10.0 Otorisasi iOS lewat FamilyControls

Dua keputusan dalam daftar itu menanggung sebagian besar bebannya.

Menyentralkan model izin

Pada versi 0.3.0 sudah ada tiga alur izin terpisah — akses pemakaian, aksesibilitas, dan overlay — masing-masing dengan jalur permintaan sendiri, layar pengaturan sendiri, dan cara pemeriksaan sendiri. Pemanggil harus menulis urutan kikuk yang sama tiga kali.

Versi 0.4.0 meringkasnya menjadi sepasang metode di atas satu enum:

// Satu bentuk untuk setiap izin, seberapa pun berbedanya implementasi platform.
final granted = await screenTime.requestPermission(
  permissionType: ScreenTimePermissionType.appUsage,
);
final status = await screenTime.permissionStatus(
  permissionType: ScreenTimePermissionType.accessibilitySettings,
);

Enum berisi appUsage, accessibilitySettings, drawOverlay, dan notification. Maknanya bergantung pada platform. Di iOS, tiga nilai pertama memetakan permintaan dan status ke otorisasi Family Controls yang sama; nilai tersebut tidak memberikan akses aksesibilitas atau overlay seperti di Android. Notifikasi memakai UNUserNotificationCenter secara terpisah. Pemetaan ini terlihat dalam implementasi izin iOS.

Memperlakukan pemblokiran sebagai pekerjaan latar terjadwal, bukan loop yang berjalan

Pemblokiran tidak boleh bergantung pada aplikasi yang masih hidup. Implementasi Android-nya dibangun dari worker WorkManager dan receiver sistem, bukan foreground loop yang berumur panjang — worker untuk menerapkan, melanjutkan, dan mencabut blokir; boot receiver agar jadwal bertahan setelah restart; alarm receiver untuk aturan berbasis waktu; dan worker pemantau yang menyalakan ulang layanan jika sistem mematikannya.

Itu jauh lebih banyak mesin daripada sekadar "jalankan service". Itu juga yang membedakan fitur yang bekerja dari fitur yang bekerja sampai ponselnya di-reboot.

Tantangan

Bug yang layak dilaporkan adalah yang hanya muncul dengan data nyata.

Overflow bilangan yang senyap. appUsageData menerima waktu mulai dan selesai sebagai int Dart. Menanyakan rentang tanggal lama mendorong milidetik epoch melewati batas yang bisa dikonversi platform channel menjadi int 32-bit, sehingga menghasilkan type error, bukan angka yang salah. Perbaikannya: menerima Number di sisi native — perubahan satu baris yang hanya muncul karena ada yang menanyakan data cukup jauh ke belakang.

Kueri pemakaian yang mengembalikan hari yang salah. Menanyakan tanggal tanpa catatan screen time mengembalikan data dari hari sebelumnya yang ada datanya, bukan nol. Itu jawaban yang salah parah tapi berkostum masuk akal: dasbor pemakaian akan diam-diam menampilkan angka kemarin di bawah tanggal hari ini. Perbaikannya: menyaring hasil kueri ke tanggal spesifik, bukan memercayai agregasinya.

Keduanya diperbaiki di 0.10.3. Keduanya jenis cacat yang tidak akan ditemukan unit test dengan data sintetis.

Trade-off

Kemampuan yang diimplementasikan plugin saat ini berbeda pada tiap platform. Kueri pemakaian, pemantauan, pemblokiran, dan penjadwalan tersedia di handler Android. Handler iOS mengembalikan FlutterMethodNotImplemented untuk panggilan tersebut. Aplikasi pemakai perlu memeriksa platform sebelum menawarkan fitur; nama metode yang sama belum membuktikan dukungan iOS.

Layanan aksesibilitas butuh penyiapan manual oleh pengguna. Tidak ada jalan memutar, jadi plugin ini mengekspos openAccessibilitySettings() dan isAppMonitoringServiceEnabled() lalu membiarkan aplikasi induk merancang proses onboarding-nya secara jujur.

Pemakai plugin mewarisi pekerjaan integrasi nyata. Layanannya harus dideklarasikan di manifest aplikasi induk beserta berkas konfigurasinya sendiri. Itu lebih banyak dari yang biasanya diminta sebuah plugin, dan itu adalah harga dari kemampuannya, bukan kelalaian.

Pekerjaan plugin terkait

Batas yang sama muncul di paket-paket lebih kecil yang dibangun dengan cara serupa: Screenshot Callback Plus — notification observer di iOS, pengamatan konten di Android — dan Flutter Dynamic Icon Plus, di mana ikon alternatif didukung secara tidak merata antar OEM Android dan angka badge iOS sama sekali tidak punya padanan di Android.

Hasil

Screen Time dipublikasikan di pub.dev. Versi 0.10.3 adalah rilis terpublikasi yang ditinjau pada 7 September 2026. Changelog mencatat pengembangan fitur Android sejak April 2025 dan penambahan metode permintaan serta status izin iOS pada Mei 2025.

Hasilnya adalah implementasi Android untuk pemantauan dan pembatasan penggunaan, disertai jembatan izin iOS yang lebih terbatas. Tinjauan sumber ini membuktikan keberadaan jalur kode; bukan jaminan keandalan pada semua perangkat atau kesetaraan fitur antarplatform.

Yang Saya Pelajari

Cross-platform adalah strategi pengiriman, bukan strategi rekayasa. Begitu sebuah produk meraih sistem operasi, kita sedang melakukan rekayasa platform — dan kualitas hasilnya bergantung pada pemahaman kita atas Android dan iOS secara terpisah, bukan pada seberapa mahir kita dengan Flutter.

Pelajaran kedua soal kejujuran API. Dokumentasi dan UI produk perlu menunjukkan kemampuan yang benar-benar diimplementasikan pada tiap platform. Izin mengakses framework native adalah titik awal, bukan fitur lintas platform yang sudah selesai.


Menghadapi masalah rekayasa serupa?

Ceritakan apa yang sedang Anda bangun atau apa yang rusak. — Chandra Abdul Fattah

Diskusikan dengan saya