Flutter SDK
A lightweight Flutter SDK with full cross-platform support.
Requirements
- Flutter 3.10+
- Dart 3.0+
Platform Support
| Platform | Supported |
|---|---|
| iOS | Yes |
| Android | Yes |
| Web | Yes |
| macOS | Yes |
| Windows | Yes |
| Linux | Yes |
Installation
Add to your pubspec.yaml:
dependencies:
mostly_good_metrics_flutter: ^0.8.0
Then run:
flutter pub get
For iOS privacy manifest coverage, use shared_preferences ^2.2.3, path_provider ^2.1.3, and device_info_plus ^11.1.1 or compatible newer versions. See Apple privacy manifests and App Store labels for the app-level manifest and label.
Quick Start
Initialize
Initialize once at app startup (typically in main.dart):
import 'package:flutter/material.dart';
import 'package:mostly_good_metrics_flutter/mostly_good_metrics_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MostlyGoodMetrics.configure(
MGMConfiguration(apiKey: 'mgm_proj_your_api_key'),
);
runApp(MyApp());
}
Track Events
// Simple event
MostlyGoodMetrics.track('button_clicked');
// Event with properties
MostlyGoodMetrics.track('purchase_completed', properties: {
'product_id': 'SKU123',
'price': 29.99,
'currency': 'USD',
});
Identify Users
// Set user identity
await MostlyGoodMetrics.identify('user_123');
// Reset identity (e.g., on logout)
await MostlyGoodMetrics.resetIdentity();
Configuration Options
await MostlyGoodMetrics.configure(
MGMConfiguration(
apiKey: 'mgm_proj_your_api_key',
baseUrl: 'https://ingest.mostlygoodmetrics.com',
environment: 'production',
appVersion: '1.0.0',
maxBatchSize: 100,
flushInterval: 30,
maxStoredEvents: 10000,
enableDebugLogging: kDebugMode,
trackAppLifecycleEvents: true,
),
);
| Option | Default | Description |
|---|---|---|
apiKey | Required | Your API key |
baseUrl | https://ingest.mostlygoodmetrics.com | API endpoint |
environment | "production" | Environment name |
appVersion | - | App version (required for install/update tracking) |
maxBatchSize | 100 | Events per batch |
flushInterval | 30 | Auto-flush interval in seconds |
maxStoredEvents | 10000 | Max cached events |
enableDebugLogging | false | Enable debug output |
trackAppLifecycleEvents | true | Auto-track lifecycle events |
existingInstallation | false | Baseline lifecycle state on a provider migration without emitting $app_installed |
contextProvider | - | Dynamic properties evaluated for every event |
optedOutByDefault | false | Start opted out until optIn() is called (Privacy) |
collectDeviceProperties | true | Collect device manufacturer, locale, timezone |
Automatic Events
| Event | When | Properties |
|---|---|---|
$app_installed | First launch after install | - |
$app_updated | First launch after version change | previous_version, current_version |
$app_opened | App started | - |
$app_foregrounded | App became active | - |
$app_backgrounded | App went to background | - |
Migration and dynamic context
For an existing app moving from another provider, derive existingInstallation
from that provider's persisted installation marker. Do not set it to true
for every user, which would suppress genuine new installs.
await MostlyGoodMetrics.configure(MGMConfiguration(
apiKey: 'mgm_proj_your_api_key',
existingInstallation: legacyAnalytics.hasInstallationMarker,
contextProvider: () => {'active_workspace_id': currentWorkspaceId()},
));
Dynamic context overrides super properties; explicit event properties and MGM
system properties take precedence. DEBUG builds report invalid event names and
reserved custom $ property keys.
Session Management
// Start a new session manually
await MostlyGoodMetrics.startNewSession();
// Access current session ID
final sessionId = MostlyGoodMetrics.sessionId;
Privacy
The SDK never collects advertising identifiers (IDFA/GAID), precise location, contacts, or any other personal data you don't explicitly pass to track() or identify(). identify() is entirely optional — without it, users are tracked under a random anonymous ID ($anon_...) generated by the SDK, not derived from the device.
Opt-out
// Stop all tracking immediately. track(), identify(), and flush() become
// no-ops, and any queued (unsent) events are deleted. The choice is
// persisted and survives app restarts.
await MostlyGoodMetrics.optOut();
// Check the current state
final optedOut = MostlyGoodMetrics.isOptedOut;
// Resume tracking (also persisted)
await MostlyGoodMetrics.optIn();
For consent-first apps (e.g. GDPR), start opted out and only opt in after consent:
await MostlyGoodMetrics.configure(
MGMConfiguration(
apiKey: 'mgm_proj_your_api_key',
optedOutByDefault: true,
),
);
// Later, once the user consents:
await MostlyGoodMetrics.optIn();
A persisted opt-in/opt-out choice always takes precedence over optedOutByDefault.
Rotating the anonymous ID
Generate a fresh anonymous ID so future events can't be linked to earlier anonymous activity:
await MostlyGoodMetrics.resetAnonymousId();
Forget me
For a complete local reset (e.g. a user deletes their account):
await MostlyGoodMetrics.resetIdentity(clearAnonymousId: true);
This clears the user ID, rotates the anonymous ID, deletes all pending (unsent) events, clears super properties and sticky local experiment assignments, and starts a new session.
Limiting device properties
await MostlyGoodMetrics.configure(
MGMConfiguration(
apiKey: 'mgm_proj_your_api_key',
collectDeviceProperties: false,
),
);
When disabled, events omit device manufacturer, locale, and timezone. Platform, OS version, and app version are still included.
Error Handling
try {
MostlyGoodMetrics.track('invalid-event-name');
} on MGMError catch (e) {
print('Error type: ${e.type}');
print('Message: ${e.message}');
}
Error types:
MGMErrorType.notConfigured- SDK not configuredMGMErrorType.invalidEventName- Invalid event nameMGMErrorType.invalidProperties- Invalid propertiesMGMErrorType.networkError- Network failureMGMErrorType.rateLimited- API rate limited
Manual Flush
await MostlyGoodMetrics.flush();
// Check pending events
final count = await MostlyGoodMetrics.getPendingEventCount();
print('$count events pending');
// Clear pending events
await MostlyGoodMetrics.clearPendingEvents();