# Raygun

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Add Raygun crash reporting to a FlutterFlow app by creating a Custom Action that adds the raygun4flutter pub.dev package, initializes it with your Raygun application API key, and wires global Dart error handlers. Call this initRaygun() action as a Final Action on app launch. Because FlutterFlow's generated main.dart cannot be directly edited, the global FlutterError.onError and PlatformDispatcher.onError handlers must be set inside the Custom Action. Test only on real devices, not the web Preview.

## Add Crash Reporting to Your FlutterFlow App with Raygun

When you ship a FlutterFlow app to users, you lose visibility into what breaks. Users rarely file bug reports; they just uninstall. Raygun solves this by automatically capturing uncaught Dart exceptions and sending them to a hosted dashboard where you can see stack traces, affected devices, OS versions, and the exact sequence of events that led to a crash — without the user doing anything.

Raygun offers both Crash Reporting and Real User Monitoring (RUM) for performance. The Crash Reporting tier has a free trial followed by paid plans starting from approximately $4 per month for the entry tier (verify current pricing at raygun.com/pricing — these change regularly). The Flutter SDK — raygun4flutter — is an official Raygun package on pub.dev that wraps the platform-level crash capture APIs.

The implementation challenge specific to FlutterFlow is that Raygun's Flutter SDK normally initializes in main.dart — the app entry point — where it can register global error zones (runZonedGuarded) and catch all uncaught exceptions before they terminate the app. FlutterFlow generates main.dart automatically and does not expose it for editing. The workaround is a Custom Action called early in the app's lifecycle (as a Final Action on the root page's initialization) that sets FlutterError.onError and PlatformDispatcher.onError — two hooks that catch most Flutter and Dart errors. This approach catches the vast majority of crashes with only a small edge-case gap compared to the full runZonedGuarded approach.

## Before you start

- A Raygun account with a new Application created for your FlutterFlow app — copy the Application API key from the Raygun dashboard
- A FlutterFlow project building a native iOS/Android app (crash reporting does not function in the FlutterFlow web Preview)
- Basic familiarity with FlutterFlow's Custom Code panel and the Action Flow Editor
- A physical iOS or Android device or simulator/emulator for testing (Run mode, not Preview mode)
- (Optional) A Raygun REST API key for reading crash data back into the app — this is a separate, read-scoped key from the crash reporting application key

## Step-by-step guide

### 1. Create a Raygun Application and get your API key

Log into your Raygun account at raygun.com. In the Raygun dashboard, click the + New Application button (or Create Application). Give it a name matching your FlutterFlow app (for example My Startup App). Select Flutter as the platform. Raygun generates an Application API key — a short alphanumeric string like AAAAAAAABBBBBBBBB. Copy this key.

This Application API key is specifically for SENDING crash data FROM the app TO Raygun. It is scoped to submission only and is considered safe to include in a client app — it cannot be used to read your crash data back out of Raygun. You will store it as a constant in your Custom Action Dart code (not in FlutterFlow App Values, which are compiled into the app in a similarly accessible way, so the security level is equivalent).

Note: if you also want to read crash events back into a FlutterFlow screen (the third use case above), you need a SEPARATE REST API key from your Raygun account settings (Account → API Keys). This read key IS sensitive and should go through a Firebase Cloud Function proxy — do not put it in FlutterFlow directly.

**Expected result:** You have a Raygun Application created for your FlutterFlow app and the Application API key copied to a safe location.

### 2. Add the raygun4flutter dependency in FlutterFlow

In FlutterFlow, open Custom Code from the left navigation panel. Look for the Pub Dependencies section (it may be a tab or a section within the Custom Code panel). Click + Add Dependency. In the package name field, type raygun4flutter. In the version field, enter the latest stable version from pub.dev — search for raygun4flutter at pub.dev to find the current version number (for example 2.0.0 or higher — verify at pub.dev/packages/raygun4flutter).

Important: do not use the latest flag (^ with no version) if you can avoid it. FlutterFlow Custom Code can have version conflicts when packages are added without explicit version pins. Pin to a specific version, and if FlutterFlow shows a dependency conflict error, try the next lower version until the conflict resolves.

After adding the dependency, click Save. FlutterFlow will validate the dependency. If no error appears, the package is correctly registered and will be available to import in your Custom Actions.

Note: the raygun4flutter package requires platform support (iOS and Android). It is NOT compatible with the FlutterFlow web Preview runtime. Attempting to use it in Preview mode will result in errors or no-ops — this is expected behavior, not a bug.

**Expected result:** The raygun4flutter package appears in the Custom Code → Pub Dependencies list without validation errors.

### 3. Write the initRaygun() Custom Action

In FlutterFlow, go to Custom Code → + Add → Action. Name the action initRaygun. Set the return type to void (this action initializes state; it does not return a value).

In the code editor, add the imports and write the initialization logic. The action sets up three error hooks:
1. FlutterError.onError — catches Flutter widget tree errors
2. PlatformDispatcher.instance.onError — catches async Dart errors that escape zone boundaries
3. The Raygun SDK's own error listener

Because FlutterFlow's main.dart is generated and you cannot add runZonedGuarded, these two hooks together catch the large majority of production crashes.

After writing the code, add the Dart import for raygun4flutter in the Imports field in the Custom Code panel:
import 'package:raygun4flutter/raygun4flutter.dart';

Save the action. You will wire it to the app lifecycle in the next step.

```
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:raygun4flutter/raygun4flutter.dart';

Future<void> initRaygun(String apiKey) async {
  // Initialize the Raygun SDK
  await Raygun.init(apiKey: apiKey);

  // Catch Flutter framework errors (widget build errors, rendering errors)
  final originalOnError = FlutterError.onError;
  FlutterError.onError = (FlutterErrorDetails details) {
    Raygun.sendException(
      throwable: details.exception,
      stackTrace: details.stack,
      tags: ['flutter-error'],
    );
    // Also report through the original handler (prints to console in debug)
    originalOnError?.call(details);
  };

  // Catch async Dart errors that escape Flutter's zone
  PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
    Raygun.sendException(
      throwable: error,
      stackTrace: stack,
      tags: ['platform-dispatcher'],
    );
    return true; // Returning true means we handled it
  };
}
```

**Expected result:** The initRaygun Custom Action is saved in FlutterFlow without compilation errors. The action accepts an apiKey String parameter and returns void.

### 4. Wire initRaygun() as a Final Action on app launch

FlutterFlow does not have an explicit app-start lifecycle hook equivalent to main.dart's runApp. The recommended pattern is to call initialization actions as Final Actions on the root page — the first page shown when the app launches.

In FlutterFlow, navigate to your root screen (usually named HomePage or SplashScreen). Open the page's properties panel and look for the On Page Load section (sometimes under the Actions tab or the page-level settings). Click + Add Action (or Open Action Flow Editor for On Page Load).

In the Action Flow Editor, click + Add Action. Select Custom Action → initRaygun from the dropdown. Set the apiKey parameter to your Raygun Application API key — you can hardcode the key directly here as a string literal, since the Application key is client-safe. Alternatively, store it in App Values (left nav → App Values → + Add Constant) as a String named raygunApiKey and reference it from there for easier management.

Now mark this action as Final: in the Action Flow Editor, look for the Action Order or Execution section and confirm the action runs as part of the page initialization, not in response to a user gesture. The action should run once and complete before the user interacts with the app.

Critical: this Custom Action will NOT run in the FlutterFlow web Preview. Use Run mode (top right → Run → Deploy) and test on a real device or emulator. You can verify Raygun is working by deliberately triggering a test exception from the next step.

**Expected result:** The initRaygun action is wired to the root page's On Page Load event. Running the app on a device initializes Raygun without errors.

### 5. Add a manual exception reporter and test the integration

Automatic crash capture handles unhandled exceptions, but many real-world issues are handled exceptions — try/catch blocks where you catch an error but want to know it happened. Add a second Custom Action for manual reporting.

In Custom Code → + Add → Action, name it reportException. Give it two String parameters: errorMessage and customData (for attaching extra context like an API response body or user action). Write the Dart code to call Raygun.sendException() with these values.

To test the integration end-to-end, temporarily add a test button on a screen that throws a deliberate exception. In the Action Flow Editor, add a Custom Action call that throws an exception inside a try/catch and then calls reportException. Run on a real device, tap the button, then open your Raygun dashboard. Within a few seconds, the error should appear in the Crash Reporting section with the device details, OS version, and stack trace.

Also test the automatic capture by forcing an uncaught exception: add an action that accesses a null value without a null check. Run on the device. Check Raygun — the uncaught exception from FlutterError.onError should appear. Once you confirm data is flowing, remove the test buttons before shipping. If you want to show crash data inside the FlutterFlow app itself, create a separate API Group calling https://api.raygun.com (with a proxied read API key) using the pattern described in the GENERATION_PROMPT — but for most use cases, the Raygun web dashboard is sufficient.

```
import 'package:raygun4flutter/raygun4flutter.dart';

Future<void> reportException(
  String errorMessage,
  String customData,
) async {
  try {
    throw Exception(errorMessage);
  } catch (e, stack) {
    await Raygun.sendException(
      throwable: e,
      stackTrace: stack,
      tags: ['handled-exception'],
      customData: {'context': customData},
    );
  }
}
```

**Expected result:** Test exceptions appear in the Raygun dashboard within a minute of being triggered on the device, with full stack traces and device metadata.

## Best practices

- Initialize Raygun as early as possible in the app lifecycle — the initRaygun Final Action should be one of the first actions on your root/splash screen so it catches errors that occur during the very first screen load.
- Create separate Raygun Applications for development and production builds — use different API keys to prevent test crashes from polluting your production error data.
- Call Raygun.setUser() after login to identify affected users for each crash — anonymous crash data is useful, but knowing that 83 of your paying customers are affected by a specific bug is far more actionable.
- Use tags in sendException() calls to categorize handled errors by feature area — for example tags: ['payment-screen', 'stripe-api'] — so you can filter Raygun's dashboard by component.
- The Application API key (crash submission) is client-safe; the REST read API key (dashboard data retrieval) is not — always proxy the read key through a Cloud Function if you display crash data in-app.
- After shipping a new version, check the Raygun dashboard within the first hour to catch any new crashes introduced by the release before they affect a large portion of your users.
- Never suppress exceptions silently in try/catch blocks without at least calling reportException() — silent catches make debugging production issues nearly impossible.

## Use cases

### Production crash visibility for a FlutterFlow app after launch

A founder ships a FlutterFlow app to 500 users and starts receiving complaints about crashes, but has no way to reproduce them. Adding Raygun crash reporting means every future uncaught exception is captured with a full stack trace, device details, and user steps — the founder opens the Raygun dashboard and sees exactly which screen and what line of code caused the crash, enabling a fix in hours instead of days.

Prompt example:

```
Add crash reporting to my FlutterFlow app so that any uncaught exception is automatically captured and sent to Raygun with the device type, OS version, and stack trace.
```

### Handled exception reporting for known risky operations

A payment screen in a FlutterFlow app calls an API that occasionally returns malformed JSON. Rather than silently swallowing the error, a Custom Action sends a handled exception to Raygun with the raw response body attached as custom data — the founder can see in the Raygun dashboard how often this happens and which API endpoint is the culprit.

Prompt example:

```
When an API call on the payment screen fails, send a handled exception to Raygun with the error message and the raw API response as custom data.
```

### In-app crash dashboard for a developer-focused FlutterFlow tool

A FlutterFlow app built for internal developer use shows a Crashes tab that fetches recent error events from the Raygun REST API, displaying a list of the most frequent exceptions sorted by occurrence count. Developers can see the crash landscape without opening the Raygun web portal.

Prompt example:

```
Build a Crashes screen that calls the Raygun REST API to fetch the 10 most recent error events and shows each one as a Card with the error message, occurrence count, and last seen timestamp.
```

## Troubleshooting

### The initRaygun Custom Action causes a MissingPluginException or does nothing in the FlutterFlow web Preview

Cause: raygun4flutter is a native package that does not support the FlutterFlow web Preview runtime. The Preview uses a web build that cannot run native Dart packages requiring platform channels.

Solution: Always test Custom Actions that use native packages (like raygun4flutter) using FlutterFlow Run mode on a real device or emulator — not the Preview tab. In the Preview, the action may silently no-op or throw a MissingPluginException, both of which are expected and not indicative of a bug in your code.

### No crashes appear in the Raygun dashboard after triggering a test exception on the device

Cause: The Raygun API key is wrong, the initRaygun action is not being called on app start, or errors are being triggered before the Raygun SDK finishes initializing.

Solution: First, confirm the Application API key in your Dart code matches exactly what is shown in the Raygun dashboard (Application → Application Settings → API Key). Second, add a print() statement immediately after Raygun.init() to confirm the init action is executing. Third, wait 60 seconds after triggering the error before checking the dashboard — Raygun sends data asynchronously. If still no data, check your device's network connectivity.

### FlutterFlow shows a dependency conflict error when adding raygun4flutter

Cause: raygun4flutter depends on other packages that conflict with versions already required by your FlutterFlow project's other packages.

Solution: Try pinning raygun4flutter to a slightly older version (check the changelog at pub.dev/packages/raygun4flutter/changelog for stable versions). If the conflict persists, check which of your other FlutterFlow custom packages is causing the conflict by temporarily removing other custom dependencies and re-adding them one by one.

### Crashes are captured but appear in Raygun without a user identifier — all crashes show as Anonymous

Cause: The Raygun SDK is not configured with the current user's identity.

Solution: After user login, add a Custom Action that calls Raygun.setUser() with the user's unique identifier (user ID or email). Call this action in the success path of your login action flow. This enables Raygun's Affected Users feature — you can see how many unique users are impacted by each crash.

```
// Add after login success:
await Raygun.setUser(userId: currentUserUid);
```

## Frequently asked questions

### Does the Raygun Application API key need to be kept secret?

The Raygun Application API key (the short key you use for crash submission) is specifically designed to be embedded in client apps. By itself, it can only SEND crash data to Raygun — it cannot be used to read, delete, or modify your Raygun account or error data. It is still best practice not to publish it openly in a public GitHub repo, but it is not as sensitive as a database password or admin token. Your Raygun REST read key (for reading data via the API) is sensitive and should be proxied.

### Why can't I just add Raygun in main.dart like the official Flutter docs show?

FlutterFlow generates main.dart automatically and does not allow you to edit it directly. The generated main.dart handles Firebase initialization, Supabase setup, and FlutterFlow's internal setup. To add Raygun's global error capture, you work around this by using a Custom Action called early in the app lifecycle, setting FlutterError.onError and PlatformDispatcher.instance.onError. This catches the majority of production crashes. The only errors missed are those thrown in async code that runs before your root page's on-load action fires — extremely rare in practice.

### Will Raygun capture crashes that happen before the root page finishes loading?

There is a brief window between app startup and when the root page's On Page Load Final Action runs, during which the Raygun SDK is not yet initialized. Crashes in this window will not be captured. If your app performs expensive operations during startup (large data loads, async initializations), consider moving those after the initRaygun action in the Action Flow to close this gap. In practice, this early window captures very few real-world crashes.

### Can I use Raygun's Real User Monitoring (RUM) feature in FlutterFlow too?

Raygun RUM tracks page load times and user interactions. In a native Flutter app, you can send timing events to Raygun RUM via the SDK. In FlutterFlow, you can call Raygun.recordBreadcrumb() inside Custom Actions on screen navigation events to add breadcrumbs to crash reports, and send timing events by recording start/end times in Custom Actions. Full automatic RUM instrumentation is more limited in FlutterFlow than in a hand-coded Flutter app due to the lack of main.dart access.

### Can RapidDev help set up Raygun crash reporting in my FlutterFlow app?

Yes. If you would rather skip the Custom Action setup, RapidDev's team builds FlutterFlow integrations including crash reporting configurations every week. Book a free scoping call at rapidevelopers.com/contact to discuss your app's monitoring requirements.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/raygun
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/raygun
