Pulse Monitoring for Flutter

Plugin Flutter untuk monitoring sesi, crash, error, HTTP, screen, event, dan informasi in-app dari dashboard Pulse. Versi plugin 0.1.5 menggunakan SDK native Android dan iOS 0.1.1.

Pemilik: Idris — idris3097@gmail.com.

Platform yang didukung

Platform Dukungan Persyaratan dan batasan
Android Ya Android 8.0 / API 26+, compileSdk 37+, Java 17. Perangkat dan emulator.
iOS Ya iOS 15+, CocoaPods dan Swift Package Manager. Perangkat arm64; simulator arm64 dan x86_64.
Web Belum Tidak ada implementasi monitoring web.
macOS, Windows, Linux Belum Tidak ada implementasi monitoring desktop.

Persyaratan Flutter: Flutter 3.41+ dan Dart >=3.11.0 <4.0.0. Pada platform yang belum didukung, start() tidak mengaktifkan monitoring.

Untuk compileSdk 37, gunakan Android Gradle Plugin 9.1.1+ dan Gradle yang kompatibel dengan versi AGP aplikasi. Lihat tabel kompatibilitas Android. Untuk iOS, gunakan Xcode yang kompatibel dengan Flutter dan binary Swift yang disertakan. Build simulator telah diverifikasi dengan Xcode 27.1. Plugin mendukung Swift Package Manager mulai versi 0.1.3, sekaligus mempertahankan dukungan CocoaPods untuk aplikasi yang belum bermigrasi.

Fitur dan cara pengumpulan

Fitur Cara penggunaan
Sesi dan metadata perangkat/aplikasi Otomatis setelah start().
Crash Android Uncaught JVM exceptions; crash NDK/sinyal belum ditangkap.
Crash iOS Diagnostics MetricKit; pengiriman mengikuti jadwal sistem Apple.
Error Flutter/Dart Teruskan melalui recordError() atau handler error aplikasi.
Metrik HTTP Dart Panggil recordHttp() dari HTTP client aplikasi. Tidak ada interceptor Dart otomatis.
Screen Pasang PulseNavigatorObserver pada Navigator, atau panggil trackScreen() secara manual.
Event bisnis Panggil trackEvent().
Maintenance Dengarkan maintenance dan tampilkan chip; detail dibuka dengan showMaintenance().
Informasi new feature/banner Feed dan presentasi informasi ditangani SDK native.
Push informasi Opsional; teruskan data FCM melalui handlePush(). Firebase dikonfigurasi aplikasi.

UI kustom dan force update: mulai 0.1.4, aktifkan customInformation: true. SDK menyediakan feed lengkap dan requiredUpdate, menjalankan pemeriksaan versi, serta menyimpan statusnya. Aplikasi menggambar komponen UI; tidak perlu membuat HTTP client, cache, timer, atau subscription topik Pulse sendiri. Mode default mempertahankan popup native yang sudah ada.

1. Instalasi

Tambahkan dependency dari pub.dev:

dependencies:
  pulse_monitoring: ^0.1.5
flutter pub get

Pada aplikasi iOS yang menggunakan CocoaPods:

cd ios
pod install
cd ..

Flutter mendaftarkan plugin secara otomatis. Tidak perlu menambahkan SDK manual ke Gradle aplikasi, Podfile, MainActivity, atau AppDelegate. Untuk CocoaPods, folder ios/.symlinks/plugins/pulse_monitoring dikelola Flutter; jangan diedit atau dimasukkan ke Git.

Pada aplikasi yang mengaktifkan SwiftPM, Flutter memilih manifest plugin secara otomatis. Tidak perlu menambahkan package plugin secara manual melalui Xcode. Flutter dapat tetap menggunakan CocoaPods untuk plugin lain yang belum mendukung SwiftPM. Ikuti panduan SwiftPM Flutter untuk pengaturan aplikasi Anda.

Android mengambil dependency native dari Maven Central. Package Flutter membawa XCFramework iOS, sehingga pemasangan tidak memerlukan akses repository GitHub.

2. Inisialisasi

Ambil workspace key dari dashboard Pulse → Pengaturan → Workspace. Pastikan aplikasi terdaftar dengan package/bundle ID dan platform yang sesuai.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:pulse_monitoring/pulse_monitoring.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Jika menggunakan Firebase, inisialisasikan Firebase terlebih dahulu.
  await PulseMonitoring.start(
    workspaceKey: const String.fromEnvironment('PULSE_WORKSPACE_KEY'),
    debug: kDebugMode,
    autoMaintenancePopups: false,
  );

  runApp(MaterialApp(
    navigatorObservers: [PulseNavigatorObserver()],
    home: const Scaffold(body: Center(child: Text('Home'))),
  ));
}
flutter run --dart-define=PULSE_WORKSPACE_KEY=pk_workspace_anda

Panggil start() sekali di main isolate, setelah Flutter binding siap dan sebelum mengirim event atau identitas user. Key kosong menonaktifkan inisialisasi; pastikan konfigurasi build production menyediakan key. Pelaporan sebelum SDK aktif diabaikan. await start() menunggu inisialisasi bridge, bukan konfirmasi data diterima server.

Opsi start() Default Kegunaan
workspaceKey Wajib Public workspace key dari dashboard.
baseUrl https://pulse-api-mobile.vercel.app/api/v1 Endpoint API lengkap, termasuk /api/v1, untuk server sendiri.
debug false Log diagnostik SDK; aktifkan saat pengembangan atau troubleshooting.
customInformation false Aktifkan controller Flutter dan pemeriksaan force update; matikan popup native otomatis.
autoMaintenancePopups false Aktifkan hanya jika ingin dialog maintenance otomatis.

Untuk dev dan production, gunakan workspace key serta endpoint yang sesuai dengan lingkungan masing-masing. --dart-define bukan penyimpanan secret; jangan masukkan credential admin, private key, atau token server ke aplikasi.

UI informasi kustom

Cukup public workspace key; alamat hosted Pulse sudah menjadi default:

await PulseMonitoring.start(
  workspaceKey: const String.fromEnvironment('PULSE_WORKSPACE_KEY'),
  customInformation: true,
);
final information = PulseMonitoring.information;

Controller tersedia setelah await start() pada Android/iOS. Jika inisialisasi atau platform tidak tersedia, nilainya dapat null. Dengarkan controller dengan ListenableBuilder atau addListener() dan render:

State/API Perilaku UI aplikasi
maintenance Chip persisten; ketuk untuk detail, tanpa modal otomatis.
banners Banner inline; dismissBanner(item) menyembunyikan selama proses berjalan.
nextFeature Dialog/carousel; panggil markSeen(item) setelah ditampilkan, sekali per revisi.
requiredUpdate Bottom sheet wajib; isDismissible: false, enableDrag: false, dan PopScope(canPop: false).
refresh() Refresh feed native dan versi, misalnya pull-to-refresh.

PulseInformationItem membawa halaman, gambar, CTA, prioritas, jadwal, dan revisi. PulseRequiredUpdate membawa versi, build, catatan rilis, dan URL toko. Aplikasi membuka storeUrl dan tetap mempertahankan sheet saat kembali dari toko sampai requiredUpdate null. Dahulukan force update dari pengumuman fitur; jangan menganggap berhasil membuka toko berarti aplikasi telah diperbarui.

SDK native tetap mengelola feed, cache informasi, targeting, expiry dan topik push. Controller Flutter memeriksa versi saat start, foreground, push dan setiap menit selama foreground, menyimpan kewajiban update per workspace/package/platform/build, dan menyimpan revisi fitur yang sudah dilihat. Kegagalan jaringan tidak menghapus update wajib yang sudah diketahui. Instalasi baru tanpa hasil cek sebelumnya belum dapat mengetahui kewajiban update saat offline; pembatasan operasi penting juga perlu ditegakkan server. Tidak ada dependency Firebase Dart baru; teruskan push melalui PulseMonitoring.handlePush(message.data) dari aplikasi.

Penarikan notifikasi (0.1.5+)

Backend Pulse dapat mengirim kontrol data-only type=information, action=retract, information_id, dan retired_revision. Kontrol ini tidak memiliki alert atau suara. SDK baru berlangganan topik kapabilitas pulse.<package>.<platform>.retract_v1 agar aplikasi lama tidak menerima kontrol yang belum dikenali. Sebelum menampilkan notifikasi lokal, teruskan data di callback FCM background:

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  if (await PulseMonitoring.handleBackgroundPush(message.data)) return;
  // Penanganan notifikasi aplikasi lainnya.
}

RemoteMessage dan Firebase berasal dari dependency Firebase aplikasi. Handler ini dapat berjalan sebelum PulseMonitoring.start() dan tidak meminta workspace key lagi. Gunakan juga sebelum penanganan foreground; handlePush() mengenali kontrol yang sama untuk alur foreground biasa. Jangan menampilkan notifikasi lokal untuk kontrol yang sudah ditangani.

Pada customInformation: true, bridge native menyaring revisi cache yang sudah ditarik dan controller melakukan rekonsiliasi notifikasi saat start, foreground, dan refresh. SDK hanya membersihkan notifikasi yang dipastikan tidak aktif dari respons feed valid, serta tidak menyamakan kegagalan API dengan feed kosong. Aplikasi perlu menutup sheet sendiri ketika item tidak lagi ada dalam controller. UI native lama/default dan paket standalone Android/iOS tetap memakai binary monitoring sebelumnya; jangan mengasumsikan popup native lama memiliki seluruh aturan penarikan UI kustom Flutter ini.

Android memakai tag pulse.info.<information_id>.<revision> dari backend dan NotificationManager.cancel(). Notifikasi Android lama tanpa tag tersebut tidak bisa dipetakan dengan aman. iOS mencocokkan payload information_id dan information_revision, lalu menghapus delivered/pending requests terkait. Notifikasi iOS legacy dengan ID yang cocok tetapi tanpa revisi diperlakukan sebagai revisi 0. Notifikasi lain dan revisi yang lebih baru tidak dihapus.

Penarikan merupakan best effort: perangkat offline, force-stop Android, atau pembatasan background/silent push iOS dapat menunda penarikan. Pada iOS, aktifkan Background Modes → Remote notifications, konfigurasi APNs/Firebase, dan teruskan handler background aplikasi. Backend menyimpan antrean penarikan terpisah agar penghapusan item tidak menghilangkan perintahnya. Data yang sudah dibaca pengguna tidak dapat dibatalkan. Tidak ada jaminan perangkat telah menghapus notifikasi hanya karena FCM menerima pesan.

3. Identitas user dan event

Sesudah login berhasil, gunakan ID internal yang stabil. Bersihkan identitas ketika logout agar event selanjutnya tidak terkait akun sebelumnya.

PulseMonitoring.setUser('user_123');

PulseMonitoring.trackEvent('document_sent', {
  'document_count': 2,
  'delivery_method': 'email',
  'success': true,
});

// Saat logout:
PulseMonitoring.setUser(null);

Gunakan nama event yang konsisten dan properties sederhana: string, angka, atau boolean. Hindari email, nama lengkap, isi dokumen, token, dan data sensitif lain.

Untuk screen, PulseNavigatorObserver membaca RouteSettings.name pada PageRoute. Pasang observer pada Navigator yang digunakan router aplikasi dan beri nama route. Jika router tidak memberikan nama, laporkan screen secara manual:

PulseMonitoring.trackScreen('document_detail');

Gunakan satu mekanisme pelaporan untuk screen yang sama agar tidak duplikat.

4. Error Flutter dan error yang ditangani aplikasi

Plugin tidak memasang handler error Flutter secara otomatis. Untuk error yang sudah ditangani, laporkan setelah aplikasi menentukan cara pemulihannya:

try {
  // Operasi aplikasi.
} catch (error, stack) {
  PulseMonitoring.recordError(error, stack);
  // Tampilkan pesan atau lakukan pemulihan sesuai alur aplikasi.
}

Untuk menghubungkan error global, pasang handler sekali setelah start(). Pertahankan handler yang sudah dipasang aplikasi atau Crashlytics:

import 'dart:ui' show PlatformDispatcher;
import 'package:flutter/foundation.dart' show FlutterError;
import 'package:pulse_monitoring/pulse_monitoring.dart';

void installPulseErrorHandlers() {
  final previousFlutterError = FlutterError.onError;
  FlutterError.onError = (details) {
    PulseMonitoring.recordError(details.exception, details.stack);
    if (previousFlutterError != null) {
      previousFlutterError(details);
    } else {
      FlutterError.presentError(details);
    }
  };

  final previousPlatformError = PlatformDispatcher.instance.onError;
  PlatformDispatcher.instance.onError = (error, stack) {
    PulseMonitoring.recordError(error, stack, fatal: true);
    return previousPlatformError?.call(error, stack) ?? false;
  };
}

Sesuaikan fatal dengan kebijakan error aplikasi. Jangan melaporkan exception yang sama berulang melalui catch, zone, dan handler global. Error dari isolate lain perlu diteruskan melalui mekanisme error isolate milik aplikasi. Lihat penanganan error Flutter.

5. Metrik HTTP

Tambahkan recordHttp() pada HTTP client/interceptor yang sudah dipakai aplikasi. Contoh berikut memakai package:http; tambahkan dependency http jika diperlukan.

import 'package:http/http.dart' as http;
import 'package:pulse_monitoring/pulse_monitoring.dart';

Future<http.Response> monitoredGet(http.Client client, Uri uri) async {
  final watch = Stopwatch()..start();
  try {
    final response = await client.get(uri).timeout(const Duration(seconds: 15));
    PulseMonitoring.recordHttp(
      method: 'GET',
      url: uri,
      status: response.statusCode,
      duration: watch.elapsed,
      responseBytes: response.bodyBytes.length,
    );
    return response;
  } catch (error) {
    PulseMonitoring.recordHttp(
      method: 'GET',
      url: uri,
      status: 0,
      duration: watch.elapsed,
      error: error.runtimeType.toString(),
    );
    rethrow;
  } finally {
    watch.stop();
  }
}

SDK membuang credentials URL, query string, dan fragment sebelum mengirim metrik melalui bridge. Path URL serta string error tetap perlu diperiksa oleh aplikasi; jangan memasukkan ID sensitif atau isi respons ke dalamnya. API recordHttp() tidak menerima request/response body atau header. Jangan membuat request monitoring kedua jika client aplikasi sudah melaporkan request yang sama. Gunakan kembali HTTP client milik aplikasi dan tutup client ketika service atau pemiliknya di-dispose, sehingga koneksi dapat dipakai kembali antar-request.

6. Maintenance chip

PulseMonitoring.maintenance menyediakan ValueNotifier<PulseMaintenance?>. Nilainya null ketika tidak ada maintenance aktif. Biarkan SDK mengelola notifier ini pada aplikasi production.

ValueListenableBuilder<PulseMaintenance?>(
  valueListenable: PulseMonitoring.maintenance,
  builder: (context, maintenance, child) {
    if (maintenance == null) return const SizedBox.shrink();
    return ActionChip(
      avatar: Icon(Icons.info_outline, color: maintenance.color),
      label: Text(maintenance.title),
      onPressed: PulseMonitoring.showMaintenance,
    );
  },
)

Import package:flutter/material.dart untuk widget di atas. Pertahankan autoMaintenancePopups: false jika memakai chip, sehingga maintenance tidak membuka dialog otomatis. Menekan chip membuka detail maintenance melalui SDK native. Untuk dialog otomatis, set autoMaintenancePopups: true saat inisialisasi.

7. Push informasi dengan Firebase (opsional)

Monitoring dasar tidak memerlukan Firebase. Untuk push, aplikasi perlu memasang serta mengonfigurasi firebase_core dan firebase_messaging, permission notifikasi, dan konfigurasi APNs/FCM sesuai platform. Setelah Firebase dan Pulse siap, teruskan pesan saat foreground dan saat notifikasi dibuka:

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:pulse_monitoring/pulse_monitoring.dart';

void routeMessage(RemoteMessage message) {
  if (PulseMonitoring.handlePush(message.data)) return;
  // Lanjutkan handler pesan aplikasi yang lain di sini.
}

Future<void> configurePulsePush() async {
  FirebaseMessaging.onMessage.listen(routeMessage);
  FirebaseMessaging.onMessageOpenedApp.listen(routeMessage);
  final initial = await FirebaseMessaging.instance.getInitialMessage();
  if (initial != null) routeMessage(initial);
}

handlePush() mengenali data type: information dan meminta SDK memperbarui feed informasi. Nilai true berarti pesan telah diteruskan, bukan konfirmasi popup sudah terlihat. Aturan maintenance tetap mengikuti autoMaintenancePopups. Daftarkan listener sekali dan batalkan subscription ketika pemiliknya di-dispose. Plugin ini digunakan dari main isolate; jangan memanggilnya langsung dari background isolate FCM. Untuk konfigurasi lengkap, ikuti panduan penerimaan pesan Firebase.

Praktik integrasi yang disarankan

  • Mulai SDK sebelum mengirim event, lalu set user setelah session login tersedia.
  • Gunakan ID internal dan properties yang tidak memuat data sensitif.
  • Pertahankan handler error dan alur push aplikasi yang sudah ada; hindari laporan duplikat.
  • Gunakan observer route atau pelaporan screen manual sesuai router aplikasi.
  • Biarkan antrean dan retry SDK berjalan; jangan memanggil flush() pada setiap event.
  • Gunakan PulseMonitoring.flush() untuk meminta upload segera saat diperlukan. Pemanggilan ini tidak menunggu upload selesai atau menjamin server menerima data.
  • Validasi pengiriman melalui dashboard pada perangkat uji. Antrean offline memiliki batas kapasitas; SDK tidak menjamin pengiriman setiap event.
  • Uji Android release dengan minification aktif serta iOS pada perangkat fisik. Simulator saja tidak membuktikan penerimaan diagnostics MetricKit.

SDK menyertakan PrivacyInfo.xcprivacy pada framework iOS. Deklarasi privasi aplikasi perlu mencerminkan identitas user, event custom, dan data lain yang benar-benar dikirim. Diagnostics crash iOS mengikuti jadwal Apple dan tidak dijamin muncul saat launch berikutnya. Android belum menangkap crash NDK/sinyal. Gunakan crash reporter lain sesuai kebutuhan aplikasi.

Upgrade dan troubleshooting

Untuk memperbarui package yang sudah terpasang:

flutter pub upgrade pulse_monitoring
cd ios
pod update pulse_monitoring --no-repo-update
cd ..
Masalah Pemeriksaan
checkReleaseAarMetadata menyebut android-36 Gunakan plugin 0.1.2+ dan compileSdk aplikasi 37+. Periksa versi yang terpilih pada pubspec.lock.
SDK tidak mengirim data Pastikan workspace key terisi, endpoint dapat diakses, serta package/bundle ID terdaftar pada workspace yang benar.
Maintenance chip tidak muncul Pastikan konten published, jadwal aktif, target platform/package cocok, dan feed sudah diterima. Periksa juga ID flavor dev/prod.
Screen tidak tercatat Beri nama route atau panggil trackScreen() manual.
Error/HTTP Dart tidak tercatat Hubungkan handler error dan HTTP client seperti contoh di atas.
Error MissingPluginException setelah menambah dependency Hentikan aplikasi, jalankan pub get/Pods, lalu build ulang penuh; hot reload tidak mendaftarkan plugin native baru.
Push tidak muncul Periksa Firebase/APNs, permission, data type: information, serta forwarding pesan pada main isolate.
Crash iOS belum masuk dashboard Periksa diagnostics pada perangkat; pengiriman MetricKit dapat tertunda.

Versi native, contoh, dan lisensi

  • Plugin Flutter: 0.1.5.
  • Android native: io.github.mhmadidris:pulse-monitoring:0.1.1 dari Maven Central.
  • iOS native: XCFramework 0.1.1, termasuk privacy manifest dan Swift interfaces.
  • Contoh pemakaian: example/lib/main.dart.

Versi plugin dapat naik sendiri untuk perbaikan integrasi tanpa mengganti SDK native. Source implementasi Swift SDK, dSYM, dan archive Xcode tidak disertakan dalam package Flutter. Bridge Flutter dan binary iOS ikut tersedia pada pub.dev; repository GitHub dapat tetap private.

Penggunaan plugin dan binary iOS tunduk pada LICENSE. SDK Android native yang didistribusikan terpisah menggunakan Apache License 2.0. Hubungi idris3097@gmail.com untuk izin penggunaan atau pertanyaan integrasi.

Libraries

pulse_monitoring