arm_tooling 0.1.0 copy "arm_tooling: ^0.1.0" to clipboard
arm_tooling: ^0.1.0 copied to clipboard

Reusable client-side automated remote monitoring for Flutter web and mobile apps.

arm_tooling #

arm_tooling is a Flutter package for low-overhead automated remote monitoring in deployed apps.

It captures handled and unhandled failures, fingerprints recurring issues, stores per-incident cases in Cloud Firestore, and can attach screenshots through Firebase Storage. It is designed for Flutter web and mobile apps that want production telemetry without introducing a custom server just for crash and incident intake.

Features #

  • Captures FlutterError, PlatformDispatcher, and zone-level async failures.
  • Records breadcrumb history from nearby app events and print() output.
  • Deduplicates related failures into stable issue IDs.
  • Stores per-occurrence cases with context, tags, and optional recovery snapshots.
  • Optionally uploads screenshots to Firebase Storage.
  • Returns a case ID for moderate-or-higher incidents so apps can show a support reference to the user.

Documentation #

  • Installation guide
  • Usage guide

Installation #

Add the package to your app:

dependencies:
  arm_tooling: ^0.1.0

If your app does not already use them, add the Firebase packages required by your chosen sink:

dependencies:
  firebase_core: ^3.15.2
  cloud_firestore: ^5.6.12
  firebase_storage: ^12.4.10

Then fetch dependencies:

flutter pub get

Firebase setup #

arm_tooling is intentionally storage-agnostic at the API layer, but the included FirebaseArmSink expects:

  1. Firebase to be initialized before use.
  2. Cloud Firestore to be enabled.
  3. Firebase Storage to be enabled only if you want screenshot uploads.
  4. Security rules in the consuming app that allow:
    • case writes from the client
    • issue-summary reads and writes for deduplication
    • admin-only reads for full incident details

Recommended collections:

  • armIssues/{issueId}: lightweight deduplicated issue summary.
  • armCases/{caseId}: full incident records with breadcrumbs, stack traces, snapshots, and optional screenshot metadata.

Quick start #

Create a shared ArmClient:

import 'package:arm_tooling/arm_tooling.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/foundation.dart';

ArmClient createArmClient() {
  return ArmClient(
    sink: FirebaseArmSink(
      firestore: FirebaseFirestore.instance,
      storage: FirebaseStorage.instance,
    ),
    appId: 'my_flutter_app',
    environment: kReleaseMode ? 'production' : 'debug',
    userIdProvider: () => FirebaseAuth.instance.currentUser?.uid,
    userEmailProvider: () => FirebaseAuth.instance.currentUser?.email,
    routeProvider: () => AppRouteContext.instance.currentRoute,
    contextBuilder: () => AppRouteContext.instance.snapshot(),
  );
}

Wrap app startup so unhandled failures are captured:

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

  final armClient = createArmClient();

  await ArmBootstrap.runGuarded(
    client: armClient,
    body: () async {
      runApp(MyApp(armClient: armClient));
    },
  );
}

Expose the client through your app's dependency injection:

return Provider<ArmClient>.value(
  value: armClient,
  child: const MyRootView(),
);

Tracking a risky operation #

Use runTracked() around writes, submissions, checkout steps, or any action where you want a case ID and recovery context if something fails:

final armClient = context.read<ArmClient>();

await armClient.runTracked<void>(
  feature: 'lead_capture',
  operation: 'submit_inquiry',
  severity: ArmSeverity.moderate,
  category: 'data_integrity',
  tags: <String, dynamic>{
    'projectSlug': projectSlug,
    'channel': selectedChannel,
  },
  recoverySnapshotBuilder: () => <String, dynamic>{
    'form': <String, dynamic>{
      'name': nameController.text.trim(),
      'email': emailController.text.trim(),
    },
  },
  action: () async {
    await repository.submitInquiry(...);
  },
  onReported: (result) {
    if (result.caseIdExposed) {
      debugPrint('Support reference: ${result.caseId}');
    }
  },
);

Adding screenshot capture #

Wrap the part of the UI you want to capture in ArmCaptureBoundary and pass the controller's capturePng callback to runTracked() or captureException():

final boundaryController = ArmCaptureBoundaryController();

ArmCaptureBoundary(
  controller: boundaryController,
  child: YourScreenBody(),
);
await armClient.runTracked<void>(
  feature: 'checkout',
  operation: 'submit_payment',
  severity: ArmSeverity.serious,
  category: 'ui_failure',
  screenshotCapture: boundaryController.capturePng,
  action: () async {
    await checkoutRepository.submit(...);
  },
);

Screenshot uploads are best-effort. If Storage is not configured or the upload fails, the case is still recorded in Firestore.

Capturing handled exceptions directly #

If you already have a catch block and want to record the exception explicitly:

try {
  await syncJob.run();
} catch (error, stackTrace) {
  final result = await armClient.captureException(
    error: error,
    stackTrace: stackTrace,
    feature: 'background_sync',
    operation: 'pull_remote_state',
    severity: ArmSeverity.low,
    category: 'sync',
    handled: true,
  );

  debugPrint('ARM case: ${result.caseId}');
}

Data model #

FirebaseArmSink writes:

  • an issue summary document keyed by fingerprint hash
  • a case document per occurrence

The issue summary is intentionally lightweight so the client can perform deduplication checks without exposing full stack traces or recovery payloads. Full diagnostic detail stays in the case document.

Example app #

A publishable example lives in example/lib/main.dart. Replace the placeholder Firebase options with your own project values before running it.

Publishing notes #

Before publishing, run:

dart pub publish --dry-run

Also make sure:

  • LICENSE matches how you want other packages to consume this library.
  • CHANGELOG.md reflects the version you are publishing.
  • homepage, repository, and issue_tracker point to the final public repo location.
  • Your package name is available on pub.dev.
0
likes
120
points
121
downloads

Documentation

API reference

Publisher

verified publisherdev.obsivision.com

Weekly Downloads

Reusable client-side automated remote monitoring for Flutter web and mobile apps.

Topics

#monitoring #telemetry #crash-reporting #firestore

License

GPL-3.0 (license)

Dependencies

cloud_firestore, crypto, firebase_storage, flutter

More

Packages that depend on arm_tooling