Quick start: Flutter¶
On this page you send your first event and see it in your app's log. You don't need an account with any analytics service yet: you start with Herald's log module, which prints each event instead of sending it. At the end you add Firebase.
Add the core library and the log module. The Flutter SDK page lists all the modules.
1. Describe what happened¶
An event is a small class in your app. It has a name, and parameters that describe what happened:
final class const CheckoutStarted(final String plan, final int seats) extends Event {
@override
String get name => 'checkout_started';
@override
Map<String, AnalyticsValue> get parameters => {'plan': .string(plan), 'seats': .int(seats)};
// Equality lets a test compare a tracked event with the one it expects.
@override
bool operator ==(Object other) =>
other is CheckoutStarted && other.plan == plan && other.seats == seats;
@override
int get hashCode => Object.hash(plan, seats);
}
Parameters keep their type. seats reaches every service as the number 3, not as the text "3",
so you can still sum and filter by it there.
2. Create Herald¶
Herald is the object your app talks to. You give it the services to send to, and it passes every
event on to each of them. Herald calls these services providers.
Here there's one provider, the log module. It has two parts: a tracker that handles events and user properties, and a service that handles everything else, such as start-up and sign-in.
// Where the log lines go: Flutter's debugPrint fits as-is.
final AnalyticsLogger logger = debugPrint;
// Handles events and user properties, using Herald's ready-made factories.
final logTracker = LogAnalyticsTrackerService(
eventTrackerFactory: CompositeLogEventTrackerFactory([
ScreenViewLogEventTrackerFactory(logger), // screen views
GenericLogEventTrackerFactory(logger), // every other event
]),
propertySetterFactory: CompositeLogPropertySetterFactory([
GenericLogPropertySetterFactory(logger),
]),
);
// Handles the rest: start-up, sign-in and sign-out, consent.
final logService = LogAnalyticsService(logger);
final herald = Herald(
providers: [
HeraldProvider(
name: 'log',
events: logTracker,
properties: logTracker,
identity: logService,
lifecycle: logService,
consent: logService,
),
],
);
Create Herald once for the whole app. Set up your app shows how to share it.
Start it in main(), before runApp. start() returns a Future, because some services set
themselves up in the background:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final herald = createHerald();
await herald.start();
runApp(MyApp(analytics: herald));
}
3. Track the event¶
Your classes don't use Herald directly. They ask for EventTrackerService, a small interface
with a single track function, and you pass Herald in its place. That keeps them easy to test:
in tests you pass a fake instead.
class CheckoutViewModel(final EventTrackerService analytics) {
Future<void> onCheckout(String plan, int seats) => analytics.track(CheckoutStarted(plan, seats));
}
Call onCheckout('pro', 3), and the log shows the event as a real service would receive it:
4. Add Firebase¶
First set up Firebase in your app the usual way, by following Firebase's guide for Flutter. Then add Herald's Firebase module:
The Firebase provider is built the same way as the log one. Its factories send screen views as
GA4's own screen_view event, and every other event under its own name:
HeraldProvider firebaseProvider(FirebaseAnalytics firebaseAnalytics) {
final tracker = FirebaseAnalyticsTrackerService(
eventTrackerFactory: CompositeFirebaseEventTrackerFactory([
ScreenViewFirebaseEventTrackerFactory(firebaseAnalytics), // GA4's reserved screen_view
GenericFirebaseEventTrackerFactory(firebaseAnalytics), // everything else, as-is
]),
propertySetterFactory: CompositeFirebasePropertySetterFactory([
GenericFirebasePropertySetterFactory(firebaseAnalytics),
]),
);
final service = FirebaseAnalyticsService(firebaseAnalytics);
return HeraldProvider(
name: 'firebase',
events: tracker,
properties: tracker,
identity: service,
lifecycle: service,
consent: service,
);
}
Add it to Herald next to the log provider:
final herald = Herald(
providers: [
HeraldProvider(
name: 'log',
events: logTracker,
properties: logTracker,
identity: logService,
lifecycle: logService,
consent: logService,
),
firebaseProvider(firebaseAnalytics), // the new line
],
);
The same CheckoutStarted now goes to both the log and Firebase, and CheckoutViewModel didn't
change at all.
Next¶
- Set up your app: several services, error reporting and dependency injection.
- Flutter SDK: every module, and what's specific to Flutter.
- Widgets: screen views and impressions from widgets.
- Philosophy: why Herald works the way it does.