Lerix Flutter SDK
The official Flutter SDK for Lerix, the developer monitoring and engagement platform. One integration gives you automatic crash and error reporting plus push notifications on iOS, Android and Web, all managed from the Lerix dashboard.
Full documentation lives at docs.lerix.dev. This README covers the essentials; the linked guides cover every option.
Features
| Feature | iOS | macOS | Android | Web | Windows | Docs |
|---|---|---|---|---|---|---|
| Automatic capture of uncaught errors | ✅ | ✅ | ✅ | ✅ | ✅ | Error Tracking |
| Manual reporting with type, severity and metadata | ✅ | ✅ | ✅ | ✅ | ✅ | Error Tracking |
| Push notifications (APNs / FCM / Web Push / WNS) | ✅ | ✅ | ✅ | ✅ | ✅ preview | Push Notifications |
| Foreground, tap and cold-start tap callbacks | ✅ | ✅ | ✅ | ✅ | Tap and cold-start tap | Push Notifications |
| Topic subscriptions | ✅ | ✅ | ✅ | ✅ | ✅ | Push Notifications |
| Custom sounds and rich images | ✅ | Sounds only | ✅ | ✅ | Images only | Push Notifications |
| Update and unsend delivered notifications | ✅ | ✅ | ✅ | ✅ | ❌ | API reference |
| Anonymous per-install user registration | ✅ | ✅ | ✅ | ✅ | ✅ | Installation |
Android push works without a google-services.json file: the SDK configures
Firebase Cloud Messaging at runtime from your project's settings.
Requirements
| Minimum | |
|---|---|
| Flutter | 3.3.0 |
| Dart | 3.3.3 |
| iOS | 11.0 |
| macOS | 10.14 |
| Android | API 21 (Android 5.0) |
| Windows | Windows 10 2004, with the Windows App Runtime 1.7 |
| Web | Any browser with Service Worker and Push API support |
Installation
Add the package to your pubspec.yaml:
dependencies:
lerix_flutter: ^0.3.1
Then run:
flutter pub get
You will need a project API key and project id. Both are shown on your project's settings page in the dashboard; see The Dashboard if you have not created a project yet.
Quick start
import 'package:flutter/material.dart';
import 'package:lerix_flutter/lerix_flutter.dart';
void main() {
Lerix.init(
url: 'https://api.lerix.dev/v1/YOUR_PROJECT_ID',
apiKey: 'YOUR_API_KEY',
projectId: 'YOUR_PROJECT_ID',
builder: () async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
},
);
}
Lerix.init runs your builder inside a guarded zone, so every uncaught
Flutter and Dart error is reported without further code. It also registers the
app and an anonymous user with your project. Push notifications are opt-in and
are set up separately (see below).
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
String |
✅ | API base URL including your project id: https://api.lerix.dev/v1/<projectId> |
apiKey |
String |
✅ | Project API key |
projectId |
String |
✅ | Project id |
builder |
void Function() |
✅ | App startup callback; call runApp here |
debugMode |
bool? |
Print SDK activity to the console | |
onError |
Function(ErrorModel)? |
Called with every report the SDK sends | |
ignoreErrors |
List<Pattern> |
Errors to drop before sending (see Filtering errors) | |
beforeSend |
LerixErrorEvent? Function(LerixErrorEvent)? |
Inspect, change or drop each report before it is sent |
Error tracking
Uncaught errors are reported automatically once Lerix.init has run. Report a
handled exception yourself with:
try {
await riskyOperation();
} catch (e, stack) {
await Lerix.throwError(
e,
stack,
bugType: BugType.crash,
bugSeverity: BugSeverity.high,
metaData: {'userId': currentUser.id, 'screen': 'checkout'},
);
}
| Parameter | Type | Required | Description |
|---|---|---|---|
exception |
dynamic |
✅ | The caught exception |
stack |
StackTrace |
✅ | Its stack trace |
bugType |
BugType |
runtimeError, logicBug, uiBug, networkError, performance, compatibility, validationError, security, crash, unknown |
|
bugSeverity |
BugSeverity |
critical, high, medium, low, unknown |
|
metaData |
Map<String, dynamic> |
Any extra context, shown with the report |
Filtering errors
Two optional Lerix.init options decide which errors are sent. They apply to
every report: uncaught Flutter, Dart and zone errors, Lerix.throwError, and
web.
Lerix.init(
// ...
ignoreErrors: [
'SocketException', // case-insensitive substring
RegExp(r'^HttpException: 40[13]'), // RegExp, matched with hasMatch
],
beforeSend: (event) {
if (event.metadata?['test'] == true) return null; // drop it
return event.copyWith(
message: event.message.replaceAll(RegExp(r'token=\S+'), 'token=***'),
);
},
builder: () => runApp(const MyApp()),
);
ignoreErrorsruns first. AStringdrops any error whose message contains it, ignoring case (empty or whitespace-only strings are skipped). ARegExpdrops any error whose message it matches.beforeSendthen gets aLerixErrorEvent(message,stack,type,severity,metadata). Return it to send it, return a changed copy fromcopyWith, or returnnullto drop it. IfbeforeSendthrows, the original report is sent; the exception is never rethrown into your app.- A dropped report is not sent and doesn't call
onError.
The SDK ignores nothing by default. Common Flutter noise (such as RenderFlex overflow) is filtered on the Lerix server.
Reports appear in the dashboard's Error Tracking module, where the Lerix AI assistant can explain a crash against your connected GitHub repository. See the Error Tracking guide and the module overview.
Push notifications
await Lerix.notifications.init();
Lerix.notifications.setOnNotificationReceived((payload) {
// Arrived while the app was in the foreground
});
Lerix.notifications.setOnNotificationTapped((payload) {
// The user tapped a notification, including taps that launched the app
debugPrint(payload.metadata.toString());
});
final granted = await Lerix.notifications.requestPermissions();
// Pass this id in `deviceTokens` when sending from the dashboard or REST API.
final deviceId = await Lerix.notifications.getDeviceId();
Topics let a single notification reach every subscribed device:
await Lerix.notifications.subscribeToTopic('promotions');
await Lerix.notifications.unsubscribeFromTopic('promotions');
| Method | Returns | Description |
|---|---|---|
init() |
Future<void> |
Initialize the notifications module. Call this first. |
requestPermissions() |
Future<bool> |
Ask the user for notification permission |
checkPermissionStatus() |
Future<String?> |
Current permission status |
getDeviceId() |
Future<String?> |
Lerix device id used to target this device |
getDeviceToken() |
Future<String?> |
Raw APNs / FCM token or Web Push subscription |
setOnNotificationReceived(cb) |
void |
Foreground notification handler |
setOnNotificationTapped(cb) |
void |
Notification tap handler |
subscribeToTopic(key) |
Future<bool> |
Subscribe this device to a topic |
unsubscribeFromTopic(key) |
Future<bool> |
Leave a topic |
Platform setup
iOS. Enable the Push Notifications capability in Xcode, upload your APNs
key in the dashboard, and forward three AppDelegate callbacks to the SDK:
import lerix_flutter
override func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
lerix_didRegisterForRemoteNotifications(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
override func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
lerix_didFailToRegisterForRemoteNotifications(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
override func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
lerix_didReceiveRemoteNotification(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
Rich images require a Notification Service Extension target. A ready-made
NotificationService.swift ships in ios/NotificationServiceExtension/.
Step-by-step instructions, including the Xcode target setup, are in
NOTIFICATIONS_SETUP_SIMPLE.md and
APNS_SETUP_iOS.md.
macOS. Push goes through APNs with the same key you uploaded for iOS; the
device registers with the macOS bundle ID. In Xcode, open macos/Runner:
- Signing & Capabilities: sign with your Apple team and add Push
Notifications (adds
com.apple.developer.aps-environment). An app signed "to run locally" can't receive pushes; macOS rejects the permission request. - Make sure the App Sandbox allows Outgoing Connections (Client)
(
com.apple.security.network.client); the SDK needs it to reach Lerix. - Forward the APNs callbacks in
macos/Runner/AppDelegate.swift:
import lerix_flutter
override func application(_ application: NSApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
lerix_didRegisterForRemoteNotifications(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
override func application(_ application: NSApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
lerix_didFailToRegisterForRemoteNotifications(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
override func application(_ application: NSApplication,
didReceiveRemoteNotification userInfo: [String: Any]) {
lerix_didReceiveRemoteNotification(application, didReceiveRemoteNotification: userInfo)
}
Windows (preview). Push goes through WNS with a Microsoft Entra ID app
registration; add its tenant ID, client ID, client secret and object ID in the
dashboard under Notifications → Settings → Windows. No Dart changes are
needed: init() creates the push channel and registers the device. Ship
Microsoft's Windows App Runtime 1.7 installer with your app. The first build
downloads the Windows App SDK from NuGet, so it needs network access. A plain
.exe receives notifications while the app is running; to receive them while
it's closed, the app needs package identity (a sparse package). Full steps:
Windows push notifications.
Linux isn't supported for push yet.
Android. No configuration is required. Custom notification sounds are
resolved from android/app/src/main/res/raw/<name> by the name you choose in
the dashboard.
Web. Copy web_sw/lerix-sw.js from this package into your app's web/
folder so it is served at /lerix-sw.js, then call init() and
requestPermissions() as above. Generate the VAPID keys on your project's
push settings page. The SDK registers the worker on its own /lerix/ scope,
so it coexists with Flutter's flutter_service_worker.js; no
--pwa-strategy flag is needed.
The complete guide, including sending from the dashboard and the REST API, is at Push Notifications.
User identity
Each install is registered as an anonymous project user. Read or reset it with:
String? userId = Lerix.getUserId();
// Recover from a stale or mismatched user, for example a device that was
// previously registered against a different Lerix project.
await Lerix.deleteUser();
await Lerix.reRegisterUser();
Link installs to your users
Call setUser after login with your own user id, and clearUser on logout.
Your backend can then send to all of a user's devices with externalUserIds.
// After login
await Lerix.setUser(user.id, identityHash: hashFromYourServer);
// On logout
await Lerix.clearUser();
The id is stored on the device and re-sent automatically if the install is
registered again. identityHash is only needed when the project requires
identity verification: it is the hex HMAC-SHA256 of the user id keyed with the
project's identity secret. Compute it on your server and pass it to the app;
never ship the identity secret in the app.
Example app
example/ is a runnable app that exercises initialization, manual
error reporting, user management and push notifications. Replace the
placeholder key and project id in example/lib/main.dart before running it.
Sending notifications from your backend
Every action available in the dashboard is also exposed over HTTPS: send, update, unsend, cancel a scheduled notification and query per-device delivery status. See the API reference and the error and success codes.
AI coding agents can drive your account through the Lerix MCP server; see Connect an AI agent.
Resources
- Documentation: docs.lerix.dev
- Dashboard: app.lerix.dev
- Website: lerix.dev
- Support: info@alqtech.com
License
Released under the MIT License. See LICENSE.