Lightweight Dart/Flutter SDK for tgram-analytics. Track events and pageviews from your Flutter app or Dart backend.
- Pre-init buffering — call
TGA.track()anywhere, even beforeinit(). Events are queued and flushed automatically. - Never throws at runtime — double
init()logs a warning, pre-init calls are buffered, network errors are swallowed. - Fire-and-forget — tracking methods are synchronous and return immediately.
- Tiny — only depends on
httpandlogging.
- A running tgram-analytics server. See the server repo for setup instructions.
- A project API key. Create one by sending
/add myapp.comto the Telegram bot. The bot replies with a key that starts withproj_.
# pubspec.yaml
dependencies:
tgram_analytics: ^0.1.0dart pub getimport 'package:tgram_analytics/tgram_analytics.dart';
// Track events anywhere — even before init:
TGA.track('signup', 'session-123', properties: {'plan': 'pro'});
// Initialize once (e.g. in main):
TGA.init('proj_xxx', 'https://analytics.example.com');
// ^ buffered events are flushed automatically with the real API key
// Track more events:
TGA.track('purchase', 'session-123', properties: {'amount': 49});
TGA.pageview('session-123', '/dashboard');Buffer events and send them in batches to reduce HTTP requests:
TGA.init('proj_xxx', 'https://analytics.example.com',
batch: BatchOptions(maxSize: 20, maxWait: Duration(seconds: 3)),
);
TGA.track('click', 'session-1');
TGA.track('scroll', 'session-1');
await TGA.flush(); // manual flushThe queue flushes automatically when maxSize is reached or maxWait elapses.
Attach persistent properties to a session. All subsequent track() and pageview() calls for that session include them:
TGA.identify('session-123', {'plan': 'pro', 'locale': 'en-US'});
TGA.track('purchase', 'session-123', properties: {'amount': 49});
// sent properties: {plan: pro, locale: en-US, amount: 49}Per-event properties override identified properties when keys conflict.
Call TGA.forget('session-123') to clear stored properties.
Initialize the singleton. apiKey must start with "proj_".
batch—false(default),true(default thresholds), or aBatchOptionsinstance.timeout— HTTP timeout (default 10 seconds).client— optionalhttp.Clientfor testing or custom configuration.
If already initialized, logs a warning and returns the existing instance.
Track a custom event. Safe to call before init() — events are buffered.
Track a pageview event. Safe to call before init().
Store properties merged into all subsequent events for this session. Safe to call before init().
Remove stored identify() properties for a session.
Send all buffered events immediately. Returns Future<void>. No-op if not initialized or batching is disabled.
Flush pending events, wait for in-flight sends, close the HTTP client, and clear the singleton so init() can be called again. Returns Future<void>.
Access the singleton instance directly. Returns null before init().
Whether init() has been called.
Clear the singleton and discard any buffered events. Intended for testing.
Unlike most analytics SDKs that throw or silently drop events before initialization, this SDK buffers them:
// App startup — tracking happens before init is called:
TGA.track('app_open', sessionId);
TGA.identify(sessionId, {'device': 'iPhone 15'});
// Later, when config is available:
TGA.init('proj_xxx', 'https://analytics.example.com');
// All buffered events are flushed with the correct API key.
// Timestamps reflect when the events actually occurred, not when they were flushed.This is modeled after Segment's analytics-flutter SDK, which is the only major analytics SDK that implements Dart-level event buffering.
Analytics should never break your app. All HTTP and network errors are caught and logged via Dart's logging package under the tgram_analytics logger:
import 'package:logging/logging.dart';
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.loggerName}: ${record.message}');
});Only the constructor raises exceptions (on invalid apiKey or missing serverUrl).
MIT — see LICENSE.