lerix_flutter 0.4.0
lerix_flutter: ^0.4.0 copied to clipboard
The official Lerix Flutter SDK: crash reporting, push notifications, in-app feedback, NPS surveys and analytics for Flutter apps on Android, iOS, macOS, Windows and Web.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:lerix_flutter/lerix_flutter.dart';
void main() {
// Tracked before init: kept and sent once Lerix is ready.
Lerix.track('app_launching');
Lerix.init(
url: "https://api.lerix.dev/v1/YOUR_PROJECT_ID",
apiKey: "YOUR_API_KEY",
projectId: "YOUR_PROJECT_ID",
debugMode: true,
builder: () async {
WidgetsFlutterBinding.ensureInitialized();
// LerixFeedback sits above MaterialApp and hosts the feedback flow.
runApp(const LerixFeedback(child: MyApp()));
},
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Lerix Example',
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
darkTheme: ThemeData(
colorSchemeSeed: Colors.indigo,
brightness: Brightness.dark,
useMaterial3: true,
),
// Records a `$screen_view` for every named route.
navigatorObservers: [LerixNavigatorObserver()],
initialRoute: '/',
routes: {
'/': (_) => const HomePage(),
'/details': (_) => const DetailsPage(),
},
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String? _userId;
String? _deviceId;
String? _deviceToken;
@override
void initState() {
super.initState();
Lerix.notifications.setOnNotificationReceived((data) {
debugPrint("Notification Received: ${data.metadata}");
});
Lerix.notifications.setOnNotificationTapped((data) {
debugPrint("Notification tapped: ${data.metadata}");
});
_checkUserRegistration();
}
Future<void> _checkUserRegistration() async {
await Lerix.notifications.init();
final deviceId = await Lerix.notifications.getDeviceId();
final deviceToken = await Lerix.notifications.getDeviceToken();
final granted = await Lerix.notifications.requestPermissions();
final subscribed = await Lerix.notifications.subscribeToTopic('promotions');
if (!mounted) return;
setState(() {
_userId = Lerix.getUserId();
_deviceId = deviceId;
_deviceToken = deviceToken;
});
debugPrint("User ID: $_userId");
debugPrint("Permissions granted: $granted, subscribed: $subscribed");
// This is what you pass in `deviceTokens` when sending a notification
// via the dashboard or REST API — NOT the raw platform token below.
debugPrint("Device id (use this to send notifications): $_deviceId");
debugPrint("Device token (raw APNs/FCM token): $_deviceToken");
}
Future<void> _deleteUser() async {
await Lerix.deleteUser();
if (!mounted) return;
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text("User Deleted")));
}
Future<void> _openFeedback() async {
final result = await Lerix.feedback.show(
context,
const LerixFeedbackOptions(custom: {'source': 'example'}),
);
if (!mounted || !result.submitted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(result.queued
? 'Feedback saved, it will be sent when online'
: 'Feedback sent (${result.id})'),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Lerix Example"),
actions: [
IconButton(
tooltip: 'Send feedback',
icon: const Icon(Icons.feedback_outlined),
onPressed: _openFeedback,
),
],
),
body: ListView(
padding: const EdgeInsets.all(16.0),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'User Registration Status',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Text('Registered: ${_deviceId != null ? "Yes" : "No"}'),
const SizedBox(height: 4),
Text('User ID: ${_userId ?? "Not available"}'),
const SizedBox(height: 4),
// Painted over in feedback screenshots.
LerixConfidential(
child: Text('Device ID: ${_deviceId ?? "Not available"}'),
),
],
),
),
),
const SizedBox(height: 20),
const _Section('Feedback, NPS and analytics'),
FilledButton.icon(
onPressed: _openFeedback,
icon: const Icon(Icons.feedback_outlined),
label: const Text("Send feedback"),
),
const SizedBox(height: 12),
OutlinedButton(
onPressed: () => Lerix.surveys.maybeShowNps(context, force: true),
child: const Text("Show NPS survey (force)"),
),
const SizedBox(height: 12),
OutlinedButton(
onPressed: () {
Lerix.track('example_button_tapped', {'screen': 'home'});
Lerix.addBreadcrumb('Tapped the track button');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Event tracked')));
},
child: const Text("Track an event"),
),
const SizedBox(height: 12),
OutlinedButton(
onPressed: () => Navigator.of(context).pushNamed('/details'),
child: const Text("Open details screen (screen view)"),
),
const SizedBox(height: 12),
OutlinedButton(
onPressed: () => Lerix.analytics.flush(),
child: const Text("Flush analytics now"),
),
const SizedBox(height: 20),
const _Section('Users and errors'),
ElevatedButton(
onPressed: () => debugPrint("User ID: ${Lerix.getUserId()}"),
child: const Text("Print User ID to Console"),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _checkUserRegistration,
child: const Text("Check Registration Status"),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _deleteUser,
child: const Text("Delete user"),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
try {
throw Exception("Test exception");
} catch (exception, stack) {
Lerix.throwError(
exception,
stack,
bugSeverity: BugSeverity.high,
bugType: BugType.crash,
);
}
},
child: const Text("Throw Test Error"),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
try {
throw Exception("Test exception with metadata");
} catch (exception, stack) {
Lerix.throwError(
exception,
stack,
bugSeverity: BugSeverity.high,
bugType: BugType.crash,
metaData: {"name": "Test User", "email": "test@example.com"},
);
}
},
child: const Text("Throw Error With Metadata"),
),
],
),
);
}
}
class DetailsPage extends StatelessWidget {
const DetailsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details')),
body: Center(
child: FilledButton(
onPressed: () => Lerix.feedback.show(context),
child: const Text('Send feedback about this screen'),
),
),
);
}
}
class _Section extends StatelessWidget {
const _Section(this.title);
final String title;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(title, style: Theme.of(context).textTheme.titleMedium),
);
}