Skip to main content

testerpaykit 1.2.0 copy "testerpaykit: ^1.2.0" to clipboard
testerpaykit: ^1.2.0 copied to clipboard

In-app bug reports for TesterPayKit testers — screenshots, recordings and voice notes, crash capture, sessions and engaged minutes.

TesterPayKit — Flutter SDK #

Let real testers report bugs from inside your Flutter app. Testers shake the device or tap the floating button and file a report with the screenshot (annotated if they like), a screen recording or a spoken voice note. The SDK adds device and app state, the screens they went through, and crashes on its own. Reports, sessions and engaged testing minutes land in your TesterPayKit project, where you triage them and pay your testers.

You need a TesterPayKit project. The SDK sends everything to the TesterPayKit API and is tied to a project ID. Accounts are currently invite-only — to request access, write to business@moinsen.dev.

Install #

flutter pub add testerpaykit

Quick start #

Get your project ID from the TesterPayKit web console, or with the tpk CLI: tpk --json project create --name "My App" --bundle-id com.example.myapp.

Initialise the SDK before runApp — fail-safe, so a TesterPayKit problem never takes your app down:

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

const _projectId = String.fromEnvironment('TPK_PROJECT_ID');

Future<TesterPayKit?> initTesterPayKit() async {
  if (_projectId.isEmpty) return null;
  try {
    return await TesterPayKit.initialize(
      config: TesterPayKitConfig(
        projectId: _projectId,
        apiBaseUrl: 'https://api.testerpaykit.com',
        enableTesterMode: true,
        debugMode: kDebugMode,
      ),
    );
  } catch (e) {
    debugPrint('TesterPayKit disabled: $e');
    return null;
  }
}

final _navigatorKey = GlobalKey<NavigatorState>();

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final tpk = await initTesterPayKit();
  runApp(MyApp(tpk: tpk));
}

class MyApp extends StatelessWidget {
  const MyApp({required this.tpk, super.key});

  final TesterPayKit? tpk;

  @override
  Widget build(BuildContext context) {
    final sdk = tpk;
    return MaterialApp(
      navigatorKey: _navigatorKey,
      // Screen views drive the testers' engaged minutes.
      navigatorObservers: [if (sdk != null) sdk.navigatorObserver],
      builder: (context, child) {
        final content = child ?? const SizedBox.shrink();
        if (sdk == null) return content;
        return TesterPayKitWrapper(
          sdk: sdk,
          navigatorKey: _navigatorKey,
          child: content,
        );
      },
      home: const HomeScreen(),
    );
  }
}

Run it with your project ID:

flutter run --dart-define=TPK_PROJECT_ID=<project-uuid>

Shake the device or tap the floating button to file a test report, then check that it arrived: tpk bug list --bundle-id com.example.myapp.

Testers #

When the SDK has no tester identity yet, the wrapper asks for the invite code the tester got from you (through the web console). It is remembered across launches. For your own debug or CI builds, pass a code with testerId: in TesterPayKitConfig to skip that screen.

GoRouter and other Router 2.0 setups #

The navigator observer only sees Navigator 1.0 route pushes. With GoRouter, report screens yourself on every route change — otherwise engaged minutes, and with them your testers' payouts, come out too low:

TesterPayKit.instance.trackScreenView('settings');

AI test runs started from outside #

A coordinator can start an AI test run from the web console ("Start on device") or the terminal (tpk autotest --project <id>). An app built with aiRemoteTrigger: true picks the run up and lets the model drive it — nobody has to tap anything on the device:

TesterPayKitWrapper(
  sdk: sdk,
  aiRemoteTrigger: true, // off by default
  // aiRemotePollSeconds: 15,
  child: child,
)

While the app is in the foreground and no AI run is open, the wrapper asks the API every 15 seconds whether a run waits for this project. A run is handed to one app only. The overlay closes itself 3 seconds after the run ends, and polling resumes. The model gets a screenshot of the app on every step (areas wrapped in TesterSensitive stay masked); the TesterPayKit button and the AI overlay are hidden from it.

Only enable this in builds you want remote-controlled, such as your own debug or test builds. It needs a tester token for the project (the invite code above).

More API #

Call What it does
trackScreenView(name) / trackTap() Manual screen and tap tracking.
submitBugReport(description: …) File a report from code; returns a local id, uploads in the background.
submitFeedback(content: …, rating: …) General feedback.
completeTask(taskId) Mark a test task as done.
checkForUnreportedCrashes(context) Offer to report a crash from the last run.
flushAndEndSession() Upload pending data and close the session.

Platform support #

Android and iOS.

Requirements #

  • Dart ^3.11.0
  • Flutter >=3.41.0

License #

Commercial licence, not open source: you may use the SDK unmodified as a dependency of your own apps, commercial ones included, to connect them to the TesterPayKit service. Details in LICENSE.

Support #

business@moinsen.dev · testerpaykit.com