Add Sumly to a Flutter app
There is no Flutter SDK yet. Your app can send the same screens and events itself, as JSON to Sumly's events API with the public key, under RevenueCat's app user id.
Not yet run in a real app. This is the plain events API, not an SDK: there is no queue, so a screen sent while offline is lost unless your app keeps it and sends it later.
Step by step
- 1
In Sumly, open Visitors and copy your project's public key (
sl_…). Add thehttppackage to your app. - 2
Write a small function that posts a screen to
https://sumly.dev/api/v1/eventswith the key as a Bearer token and RevenueCat's app user id (await Purchases.appUserID) asuser_id, as below. - 3
Call it from a
NavigatorObserveronMaterialApp.navigatorObservers(or GoRouter'sobservers), so each route that opens counts. The screens show on the project's Sources list.
One event per call; the events API also takes up to 1,000 in one list.
import 'dart:convert';
import 'dart:io' show Platform;
import 'package:http/http.dart' as http;
import 'package:purchases_flutter/purchases_flutter.dart';
Future<void> sumlyScreen(String screen) async {
final userId = await Purchases.appUserID;
await http.post(
Uri.parse('https://sumly.dev/api/v1/events'),
headers: {'Authorization': 'Bearer sl_your_key', 'Content-Type': 'application/json'},
body: jsonEncode({'events': [
{'type': 'screen', 'screen': screen, 'user_id': userId, 'platform': Platform.isIOS ? 'ios' : 'android'},
]}),
);
}
Or let your coding assistant do it
Paste this into Claude Code, Cursor or Codex in your project. It fits the code to how your project is built, asks you for the dashboard steps and leaves the keys to you.
See what it says
Set up Flutter for Sumly in this project.
Sumly (https://sumly.dev) shows revenue next to the visits and app users it came from. The full guide is https://sumly.dev/install/flutter; read it if anything below is unclear.
Steps:
1. In Sumly, open Visitors and copy your project's public key (`sl_…`). Add the `http` package to your app.
2. Write a small function that posts a screen to `https://sumly.dev/api/v1/events` with the key as a Bearer token and RevenueCat's app user id (`await Purchases.appUserID`) as `user_id`, as into Sumly.
3. Call it from a `NavigatorObserver` on `MaterialApp.navigatorObservers` (or GoRouter's `observers`), so each route that opens counts. The screens show on the project's Sources list.
sumly.dart: One event per call; the events API also takes up to 1,000 in one list.
```
import 'dart:convert';
import 'dart:io' show Platform;
import 'package:http/http.dart' as http;
import 'package:purchases_flutter/purchases_flutter.dart';
Future<void> sumlyScreen(String screen) async {
final userId = await Purchases.appUserID;
await http.post(
Uri.parse('https://sumly.dev/api/v1/events'),
headers: {'Authorization': 'Bearer sl_your_key', 'Content-Type': 'application/json'},
body: jsonEncode({'events': [
{'type': 'screen', 'screen': screen, 'user_id': userId, 'platform': Platform.isIOS ? 'ios' : 'android'},
]}),
);
}
```
Screens: A route without a name is skipped; name your routes, or pass a name of your own.
```
class SumlyObserver extends NavigatorObserver {
@override
void didPush(Route route, Route? previousRoute) {
final name = route.settings.name;
if (name != null) sumlyScreen(name);
}
}
MaterialApp(navigatorObservers: [SumlyObserver()], …)
// or GoRouter(observers: [SumlyObserver()], …)
```
Keep in mind: A track event needs a name: `{'type': 'track', 'name': 'trial_started', 'user_id': …}`. An event older than a week, or dated in the future, is kept at the moment it arrives.
How to work:
- Look at how this project is built first, and fit the code to its framework and its way of doing things; the examples above are a starting point, not something to paste as is.
- Steps that happen in a dashboard (Sumly, or Flutter's own) are mine: tell me exactly what to click, then wait for me.
- Keys the code needs belong in environment variables, never in the code or the repository. Never ask me to paste a secret key into this chat; tell me which variable to set.
- Change only what this needs, show me the changes before you commit, and tell me how to check that it works.
- sl_your_key stands for the project's public key; it is on the Visitors page in Sumly. Ask me for it.
Consent
With no SDK, consent is your app's to keep: send nothing until the person agrees, where your app asks first.
Purchases
Purchases come from RevenueCat, Adapty or Superwall, not from your app. Send as user_id the person they know: RevenueCat's app user id, or the id you pass to Adapty or Superwall to identify the user. Each purchase then shows the screens and the source that came before it.
Your own domain
Optional: send to /api/v1/events on your own address instead, the one your site's script uses (one DNS record on Visitors, on Starter and Growth).
Good to know
- A track event needs a name:
{'type': 'track', 'name': 'trial_started', 'user_id': …}. - An event older than a week, or dated in the future, is kept at the moment it arrives.
From Flutter's own documentation, checked 4 November 2026: api.flutter.dev/flutter/widgets/NavigatorObserver-class.html, docs.flutter.dev/cookbook/networking/send-data, pub.dev/documentation/purchases_flutter/latest/purchases_flutter/Purchases/appUserID.html .
Other apps
Try it with your own projects
Sign in with Google or a code by e-mail and connect your first project. Free for one project, and 14 days of a paid plan with no card.
Start freeOr look around first: open the live demo.