An app has no script tag, so the equivalent is a package and one call. See measuring an app for what that gets you. The Skomi Flutter package depends on nothing but Flutter itself.
The source, and a demo app that reports itself, are at skomi-sdk-flutter.
Start it once
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Skomi.start(SkomiOptions(appId: 'your-app-id'));
runApp(const MyApp());
}
The app ID comes from My Apps in Skomi. It is not a secret and belongs in your source. It identifies an app rather than authenticating one, the same way a site ID does.
Name your screens
// wherever a screen becomes visible
Skomi.screen('checkout/review', props: {'tier': 'gold'});
There is no automatic capture of route names, and that is deliberate: a widget class name is what your code calls a screen, not what you would call it in a report. Up to 20 props per screen: strings, numbers, booleans.
Two things Flutter cannot tell it
Your app version. Flutter has no framework API for it, so pass it in:
final info = await PackageInfo.fromPlatform(); // package_info_plus
await Skomi.start(SkomiOptions(appId: '…', appVersion: info.version));
Leave it out and every screen reports no version, and the App version panel then says so rather than guessing, which is honest and not much use.
Where the offline queue lives. Without a path it goes in the app's temporary directory. That is app-private and survives a restart, but the OS may reclaim it under storage pressure; give it a documents path if losing a queued session matters to you.
What you get without asking
Sessions, screens, app versions, platform, device and OS breakdowns, and a crash-free rate. The queue survives being offline, quitting and crashing, and every screen carries an id minted once so a retried batch is recognised rather than counted twice.
Runs on Android, iOS, macOS, Windows and Linux. A web build compiles and collects nothing, on purpose. A Flutter web app is a site, and a site gets the script.
Errors - what it catches, and what it cannot
try {
await checkout();
} catch (error, stack) {
Skomi.captureError(error, stack: stack);
showRetry();
}
start chains FlutterError.onError and PlatformDispatcher.instance.onError, so anything you already installed keeps working. It catches errors raised inside the Flutter framework and unhandled errors on the root isolate.
⚠ It is not a crash reporter and must not be used as one. A native crash, whether a segfault in a plugin's C++, an out-of-memory kill or an NSException from a platform channel, kills the isolate that would have reported it. Keep Crashlytics, Sentry or the store's own reporting for those. What Skomi adds is the Dart half, on the same page as the errors you report yourself, and a crash-free rate you can read beside your release numbers.
Faults are grouped by type, never by message: a message carries ids, paths and amounts, so grouping on it gives one row per occurrence and a report that says nothing.
Check it is working
Run the app, move between two screens, and look at your dashboard. If nothing arrives, the usual causes are an app ID from the wrong app and a start that was never awaited.