TILLPULSE · SDK · FLUTTER

tillpulse_flutter

GA

Federated Flutter plugin (iOS + Android) for crash reporting, performance, and friction detection. Mirrors the React Native SDK's capability set in idiomatic Dart.

01Setup

Install

yaml
# pubspec.yaml
dependencies:
  tillpulse_flutter: ^0.1.0

# Then:
flutter pub get
02Init

Initialise

Wrap your app inside TillPulse.init. The appRunner argument runs your app inside a Zone that captures unhandled async errors.

dart
import 'package:tillpulse_flutter/tillpulse_flutter.dart';

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

  await TillPulse.init(
    options: TillPulseOptions(
      dsn: 'https://<key>@ingest.tilldev.dev/<project-id>',
      release: '1.4.0',
      environment: 'production',

      enableAutoFlutterErrorTracking: true,
      enableNetworkTracking: true,
      enableFrictionDetection: true,
      enablePerformanceTracking: true,

      sendDefaultPii: false,
      maxBreadcrumbs: 100,
      maxQueueSize: 500,
      flushInterval: Duration(seconds: 30),

      beforeSend: (event) {
        if (event.exceptions?.first.value?.contains('PaymentCancelled') == true) {
          return null;
        }
        return event;
      },
    ),
    appRunner: () => runApp(const MyApp()),
  );
}

With enableAutoFlutterErrorTracking: true, the SDK installs hooks on FlutterError.onError, PlatformDispatcher.instance.onError, and isolate uncaught-error listeners.

03Users

Identify users

dart
TillPulse.instance.setUser(TillPulseUser(
  id: hashedUserId,
  data: {'segment': 'pro', 'region': 'ng'},
));

TillPulse.instance.clearUser();
04Context

Tags & context

dart
TillPulse.instance.setTag('region', 'ke');
TillPulse.instance.setTag('network_type', '4g');

TillPulse.instance.setContext('device', {
  'model': 'Galaxy A54',
  'ram_mb': 4096,
});
05Capture

Manual capture

dart
try {
  await processPayment();
} catch (exception, stackTrace) {
  await TillPulse.instance.captureException(
    exception,
    stackTrace: stackTrace,
    tags: {'flow': 'payment'},
    extras: {'amount': 5000, 'currency': 'KES'},
  );
}

await TillPulse.instance.captureMessage(
  'Payment validation failed',
  level: SeverityLevel.warning,
);
08Performance

Performance spans

dart
final tx = TillPulse.instance.startTransaction(
  name: 'checkout', op: 'flow',
);

try {
  await charge();
  await tx.finish(status: SpanStatus.ok);
} catch (e) {
  await tx.finish(status: SpanStatus.internalError);
  rethrow;
}

// Or wrap a body:
await TillPulse.instance.withTransaction<void>(
  name: 'checkout',
  op: 'flow',
  body: () async { /* ... */ },
);
09Boundary

Error boundary

dart
TillPulseErrorBoundary(
  fallback: const ErrorFallbackWidget(),
  child: const PaymentScreen(),
)
10Native

Native crash handlers

The plugin installs NSSetUncaughtExceptionHandler on iOS and Thread.setDefaultUncaughtExceptionHandler on Android, plus an OOM heuristic via SharedPreferences (records an "alive" marker on init and clears on graceful close — a missing marker on next launch indicates the process was killed unexpectedly).

11Offline

Offline queue

dart
await TillPulse.instance.flush();
final size = await TillPulse.instance.getQueueSize();

Persisted to shared_preferences. Bounded by event count + total payload bytes. Backoff is exponential, max 6 attempts.

12Platform

Platform setup

Android

android/app/build.gradle
android {
  defaultConfig {
    minSdkVersion 21
  }
}

If you ProGuard / R8:

proguard-rules.pro
-keep class io.tillpulse.** { *; }
-keepattributes SourceFile,LineNumberTable
-keepattributes *Annotation*

iOS

Minimum deployment target: iOS 13.0. dSYMs are required for symbolicated stack traces. Add an Xcode Run Script phase to upload them on every release build, or use the TillDev CLI.

13Support

Troubleshooting

  • Missing frames on Android → upload mapping.txt
  • iOS frames show <redacted> → upload the .dSYM.zip
  • Events not arriving → set debug: true, check Flutter console
  • Errors in compute() isolates → SDK auto-listens to isolate uncaught errors