Installation
Raygun4Flutter adds a Flutter plugin to your app that captures and handles errors, and reports the error information to your Raygun Crash Reporting dashboard.
The plugin internally uses Raygun's Android and iOS providers to report crashes and custom errors to Raygun.
The file lib/raygun4flutter.dart
provides the main API entry point for Flutter users. Here, the plugin sets up a MethodChannel
to pass through the API calls to native Kotlin and Swift code in android/src
and ios/Classes
respectively.
Requirements
- Dart SDK 3.3.0+
As of release 1.0.0, Raygun4Flutter is built entirely in Dart and no longer relies on the native providers Raygun4Android and Raygun4Apple. We've also started to improve support for Flutter Desktop and Web. We'd appreciate any feedback on our provider Github repository.
Installation
Run this command:
$ flutter pub add raygun4flutter
This will add a line like this to your package's pubspec.yaml
(and run an implicit dart pub get
):
dependencies:
raygun4flutter: [refer to Dart package for latest version]
Alternatively, your editor might support flutter pub get
. To determine this, check the docs for your editor.
In your Dart code, you can now use:
import 'package:raygun4flutter/raygun4flutter.dart';
Platform-specific notes
If your application comprises hybrid code both using Flutter and native Android or iOS elements, please be aware that the Raygun4Flutter package will only track and report crashes from Flutter and Dart.
If you need to track crash reports across various layers and parts of your application written in different technologies, you might need to implement the relevant native providers for Android, iOS or other platforms.
Android
In your app's AndroidManifest.xml (usually located in your Flutter project's /android/src/main
directory), make sure you have granted Internet permissions.
Beneath the <manifest>
element (if it doesn't exist already) add:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Setup and usage
Initialisation and version tracking
Call Raygun.init()
with an API key to initialise RaygunClient on application start, for example, from your initState
method.
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
Raygun.init(apiKey:'paste_your_api_key_here');
}
}
The .init()
method can also accept an optional version
argument. If this is supplied, the version of your app will be tracked across Raygun crash reports:
Raygun.init(apiKey:'paste_your_api_key_here',version:'1.4.5');
As an additional convenience way to set the version, a method .setVersion()
is available. Typical use cases would most likely fall back to setting the app version in the .init()
method call when you setup the library.
Capturing and sending errors
To capture errors inside Flutter, you need to add a custom FlutterError.onError
handler to your main method. This redirects Flutter errors to Raygun.
Note: This only works when the app is running in "Release" mode.
FlutterError.onError = (details) {
// Default error handling
FlutterError.presentError(details);
// Raygun error handling
Raygun.sendException(
error: details.exception,
stackTrace: details.stack,
);
};
You can also catch Dart errors outside of the code controlled by the Flutter framework by calling to runApp
from a runZonedGuarded
and redirecting captured errors to Raygun. For example: errors that happen in asynchronous code.
Note: This works both in "Release" and "Debug" modes.
runZonedGuarded<Future<void>>(() async {
runApp(MyApp());
}, (Object error, StackTrace stackTrace) {
Raygun.sendException(
error: error,
stackTrace: stackTrace
);
});
Release
Deploy Raygun into your production environment for best results, or raise a test exception. Once we detect your first error event, the Raygun app will automatically update.
Customers
Raygun supports tracking of the unique customers who encounter bugs in your apps.
By default, Raygun will transmit a device-derived UUID. You can also add the data of the currently signed-in customer using an object of type RaygunUserInfo
:
Raygun.setUser(
RaygunUserInfo(
identifier: '1234',
firstName: 'FIRST',
fullName: 'LAST',
email: 'test@example.com',
),
);
To clear the currently logged-in customer, call setUser(null)
.
There's an additional convenience method that offers a shortcut to track your customer by an identifier only. If you use an email address to identify the user, consider using setUser
instead of setUserId
, as this will allow you to set the email address into both the identifier and email fields of the crash data.
Raygun.setUserId('1234');
Call with null
to clear the user identifier: setUserId(null)
Sending errors manually
Call Raygun.sendException(error, tags, customData, stackTrace)
to send errors to Raygun.
For example:
try {
// code that crashes
} catch (error) {
Raygun.sendException(error: error);
}
All arguments except error
are optional. This method is mainly a convenience wrapper around the more customisable .sendCustom()
method which obtains the class name and the message from the error
object.
Sending custom errors manually
Call Raygun.sendCustom(className, message, tags, customData, stackTrace)
to send custom errors to Raygun with your own customised className
and message
. As with .sendException()
, tags
, customData
and stackTrace
are optional.
For example:
Raygun.sendCustom(
className: 'MyApp',
reason: 'test error message',
tags: ['API','Tag2'],
customData: {
'custom1': 'value',
'custom2': 42,
},
stackTrace: StackTrace.current,
);
Advanced features
Tags
Raygun.setTags()
sets a list of global tags that will be logged with every exception. This will be merged with other tags passed into manually created crash reports via sendException()
and sendCustom()
.
Raygun.setTags(['Tag1','Tag2']);
Custom data
Raygun.setCustomData()
sets a global map of key-value pairs that, similar to tags, will be logged with every exception. These will be merged with other custom data passed into manually created crash reports via sendException()
and sendCustom()
.
Raygun.setCustomData({
'custom1': 'value',
'custom2': 42,
});
Breadcrumbs
Sending breadcrumbs to Raygun will provide additional information to help investigate and debug issues stemming from crash reports. Breadcrumbs can be created in two ways.
Simple string:
Call Raygun.recordBreadcrumb(message)
, where message
is just a string:
Raygun.recordBreadcrumb('test breadcrumb');
Using RaygunBreadcrumbMessage:
Create your own RaygunBreadcrumbMessage
object and send more than just a message with Raygun.recordBreadcrumb(RaygunBreadcrumbMessage)
.
The structure of the type RaygunBreadcrumbMessage
is as shown here:
RaygunBreadcrumbMessage({
required this.message,
this.category,
this.level = RaygunBreadcrumbLevel.info,
this.customData,
this.className,
this.methodName,
this.lineNumber,
});
Custom endpoints
Raygun supports sending data from Crash Reporting to your own endpoints. If you want to set custom endpoints, you can do so after you've initialised Raygun:
Raygun.setCustomCrashReportingEndpoint(url)
Please note that setting a custom endpoint will stop Crash Report or Real User Monitoring data from being sent to the Raygun backend.
Symbolification
Mobile/native - Automatic
Flutter supports code obfuscation with the --obfuscate parameter, this option generates symbol files that can be used to decode the obfuscated stack traces, as described in the section Obfuscate Dart code from the Flutter SDK.
To decode obfuscated Flutter stacktraces with Raygun, you can take advantage of our Flutter Symbols center, located within the Application Settings section of your app. From there, add the version number of your symbols and upload the particular file.
The file name should follow the following convention - app.{platform}-{architecture}.symbols
The version is required to match with the version
located in your Flutter crash reports. This informs our systems which symbol files should be considered when decoding an obfuscated crash report.
To set a version
in your crash reports, you can add the version during initialization of the client (Raygun.init
) or by calling .setVersion()
where it is avaliable. More information can be found in the Setup and usage section
note: There could be up to a 30-minute delay before the mapping process starts utilizing any uploaded files. To reprocess any errors which occur during this time simply hit the "Re-process this error for Flutter symbols" button on an error instance.
Mobile/native - Manually
If you prefer to manually decode, you can copy the stacktrace from an obfuscated crash report into a file and run the flutter symbolize
command manually. This command comes bundled with the Flutter SDK.
For example, a file named stack.txt
. It will look similar to this:
at *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** (file:///***:null)
at pid: 29207, tid: 29228, name 1.ui (unparsed:null)
at android arch: arm64 comp: yes sim: no (os::null)
at build_id: '63956a45f09b3f8410d244053eebd180' (unparsed:null)
at isolate_dso_base: 708882d000, vm_dso_base: 708882d000 (unparsed:null)
at isolate_instructions: 7088903b40, vm_instructions: 70888ed000 (unparsed:null)
at #00 abs 00000070889bcc93 virt 000000000018fc93 _kDartIsolateSnapshotInstructions+0xb9153 (unparsed:null)
at #01 abs 00000070889464fb virt 00000000001194fb _kDartIsolateSnapshotInstructions+0x429bb (unparsed:null)
... etc ...
Then, run the symbolize command passing in the copied stacktrace and the symbol file corresponding to the architecture. For example:
flutter symbolize -i stack.txt -d out/android/app.android-arm64.symbols
The decoded output will be similar to this:
at *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** (file:///***:null)
at pid: 29207, tid: 29228, name 1.ui (unparsed:null)
at android arch: arm64 comp: yes sim: no (os::null)
at build_id: '63956a45f09b3f8410d244053eebd180' (unparsed:null)
at isolate_dso_base: 708882d000, vm_dso_base: 708882d000 (unparsed:null)
at isolate_instructions: 7088903b40, vm_instructions: 70888ed000 (unparsed:null)
at #0 _MyAppState.build.<anonymous closure> (/[redacted]/raygun4flutter/example/lib/main.dart:89:17)
at #1 _InkResponseState.handleTap (//[redacted]/flutter/packages/flutter/lib/src/material/ink_well.dart:1170:21)
... etc ...
Web
Flutter web applications use dart2js
to produce a single JavaScript file main.dart.js
.
However, when reporting errors to Raygun, the reported stack traces correspond to the JavaScript generated code and not the original Dart code.
Source maps help to convert generated JavaScript code back into Dart source code. Raygun uses them to take obfuscated stack trace errors that point to generated JavaScript code and translate them to locations in Dart code.
To generate the Flutter web source maps, compile your project with the --source-maps
option.
For example:
flutter build web --source-maps
This will generate the source map file, e.g. main.dart.js.map
inside the build/web
folder.
Then, upload this file to the JS source Map center in your Raygun project's Application Settings and configure them appropriately.
You can find more information regarding source maps within our JavaScript source map section.
Uploading source maps with Raygun CLI
Raygun CLI is a command-line tool for Raygun.
Install with:
dart pub global activate raygun_cli
To upload Flutter web sourcemaps to Raygun, navigate to your project root and run the sourcemap
command with the platform
armument (-p
) to flutter
and set the uri
, app-id
and access token
parameters.
uri
is the full URI where your application main.dart.js
will be installed to.
app-id
the Application ID in Raygun.com.
token
is an access token from https://app.raygun.com/user/tokens.
raygun-cli sourcemap -p flutter --uri=https://example.com/main.dart.js --app-id=APP_ID --token=TOKEN
The input-map
argument is optional for Flutter web projects. raygun-cli
will try to find the main.dart.js.map
file in build/web/main.dart.js.map
automatically.
For more information see raygun-cli on pub.dev
The provider is open source and available at the Raygun4Flutter repository.