Herald widgets

An optional package for tracking Herald events from Flutter widgets: screen views each time a screen becomes visible, impressions when something is really on screen, and taps in widgets that have no bloc.

You don't need it to use Herald. A screen, bloc or view model can track its own events, screen views included, through EventTrackerService. Use this package when tracking from the widget tree is the simpler fit, such as counting a screen each time the user comes back to it.

dependencies:
  herald_widgets: ^1.2.0

Set it up once

final routes = HeraldRouteObserver();

runApp(
  HeraldScope(
    analytics: herald, // or your DI's EventTrackerService
    routes: routes,
    child: MaterialApp(
      navigatorObservers: [routes], // go_router: GoRouter(observers: [routes])
      home: const HomePage(),
    ),
  ),
);

Screen views, owned by each screen

@override
Widget build(BuildContext context) => TrackScreenView(
  event: ProductScreenViewed(productId),
  child: Scaffold(...),
);

It tracks each time the screen becomes visible: when it appears, when the user comes back to it, and when the app comes back to view with it on top. Rebuilds don't count, and neither do dialogs, bottom sheets or menus over it.

To track when the user leaves a screen:

TrackOnScreen(
  event: CheckoutLeft(itemsInCart: cart.length),
  on: ScreenMoment.hidden,
  child: CheckoutView(cart),
)

Taps

ElevatedButton(
  onPressed: () => HeraldScope.of(context).track(PromoBannerTapped(promo.id)),
  child: const Text('See the offer'),
)

Events that come from your app's logic belong in its bloc or view model. This is for small widgets that have none.

Impressions

TrackImpression(
  event: OfferShown(offer.id),
  threshold: 0.5, // half of it on screen
  minVisibleDuration: const Duration(seconds: 1), // for a second, not a fast scroll
  child: OfferCard(offer),
)

It counts once per appearance: something that leaves the screen and comes back counts again. Give events value equality, so a rebuild with an equal event doesn't start the count again.

Tests and previews

Wrap the widget in a HeraldScope with a FakeAnalyticsProvider from herald_testing, and add a HeraldRouteObserver for screens:

final analytics = FakeAnalyticsProvider();
final routes = HeraldRouteObserver();
await tester.pumpWidget(
  HeraldScope(
    analytics: analytics,
    routes: routes,
    child: MaterialApp(navigatorObservers: [routes], home: const ProductPage('p1')),
  ),
);

analytics.assertTracked('product', (event) => event.param('product_id', 'p1'));

A widget preview can do the same through @Preview(wrapper: ...).

Limits

  • Tabs aren't screens. Switching tabs doesn't push a page, so track it from the code that switches.
  • A nested Navigator, such as go_router's shell route, needs a HeraldRouteObserver of its own, given to a HeraldScope around it.

Documentation

It works with herald, the core, whose page lists every Herald package. The Herald website has the guides, and the repository has an example app.

Libraries

herald_widgets
Track Herald events from Flutter widgets. See the package README for the setup.