clicklens

Product analytics for Flutter apps, sent to ClickLens: a fixed-price Mixpanel alternative.

Pure Dart over HTTP. There is no native SDK to wrap, so there is no CocoaPods or Gradle work to do and the same code runs on Android, iOS, web, macOS, Windows and Linux.

Events are queued and sent in batches, retried when the network or server fails, and kept on the device across restarts — an app that goes offline mid-flight loses nothing.

flutter pub add clicklens

Getting started

import 'package:clicklens/clicklens.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ClickLens.init('YOUR_PROJECT_TOKEN');
  runApp(const MyApp());
}

Your project token is in ClickLens under Project settings → Connect your app. It is a public key: shipping it in your app is fine.

Then track what people do:

ClickLens.track('Order placed', {'total': 1200, 'currency': 'INR'});

// After login, and on each app start while logged in.
// Everything this device did anonymously is joined to the user.
ClickLens.identify(user.id);
ClickLens.setProfile({r'$email': user.email, r'$name': user.name, 'plan': 'pro'});

// On logout: a new anonymous device ID, so the next person isn't the last one.
ClickLens.reset();

Awaiting init is optional. Anything you track before it finishes loading is queued and sent after.

Properties on every event

ClickLens.registerSuperProperties({'app_version': '2.1.0', 'plan': 'pro'});
ClickLens.unregisterSuperProperty('plan');

Super properties are kept across restarts and cleared by reset().

The SDK also adds $lib, $lib_version, $os, $os_version, $device, $screen_width, $screen_height, $locale and $timezone. Turn that off with collectDeviceProperties: false.

The device model and your app's version number need a native plugin, so they are not collected. If you want them, read them with device_info_plus / package_info_plus and pass them to registerSuperProperties yourself.

Options

await ClickLens.init(
  'YOUR_PROJECT_TOKEN',
  host: 'https://ingest.clicklens.app', // self-hosting? point it here
  flushInterval: const Duration(seconds: 10),
  batchSize: 50,     // send as soon as this many events are waiting
  maxQueue: 1000,    // drop the oldest beyond this when offline for long
  collectDeviceProperties: true,
  debug: false,      // log what's sent
  storage: null,     // your own ClickLensStorage; defaults to shared_preferences
);

Queued events are sent every flushInterval, as soon as batchSize is reached, and whenever the app leaves the foreground. To send immediately:

await ClickLens.flush();

await ClickLens.close() sends what's waiting and shuts the client down. You rarely need it in an app; it's useful in tests.

Two projects at once

ClickLens is a thin static wrapper around ClickLensClient. Build your own when you need more than one:

final client = ClickLensClient('OTHER_PROJECT_TOKEN');
client.track('Order placed');

Privacy

Nothing is collected unless you track it. No IDFA, no advertising ID, no contacts, no location — ClickLens works out country and city from the IP of the request and never stores the IP.

Dependencies

  • http — the one request the SDK makes.
  • shared_preferences — the device ID, super properties and unsent events, kept across restarts.

Licence

MIT

Libraries

clicklens
ClickLens product analytics for Flutter: Android, iOS, web, macOS, Windows and Linux. Pure Dart over HTTP, so there is no native SDK to wrap and nothing to break your build.