Skip to main content
Flowboard does not force a tracking vendor. Use onStepChange and onOnboardEnd to send events to your own tools (MMP, ads SDK, product analytics).

What you can track from Flowboard callbacks

CallbackWhen it firesData you receive
onStepChange(stepId, pageStep, formData)Every time the user lands on a stepstepId (screen id), pageStep (0-based step index), formData (current snapshot of answers)
onOnboardEnd(formData)When the flow finishesformData (final collected answers)
formData keys match your Flowboard input id values. Example:
{
  "email": "ada@example.com",
  "plan": "pro",
  "accept_terms": true
}
Keep input id values stable in Flowboard. They are your analytics data contract.

Simple custom tracking examples

Use this pattern when you only need to forward events to your own tracking layer.
import { Flowboard } from 'flowboard-react';

export async function launchTrackedFlow() {
  await Flowboard.launchOnboarding({
    onOnboardEnd: (formData) => {
      console.log('Flow finished:', formData);
      myTracking.track('flow_completed', {
        plan: typeof formData.plan === 'string' ? formData.plan : undefined,
        acceptedTerms: Boolean(formData.accept_terms),
      });
    },
    onStepChange: (stepId, pageStep, formData) => {
      console.log('User moved to step:', stepId, pageStep, formData);
      myTracking.track('flow_step_viewed', {
        stepId,
        pageStep,
        plan: typeof formData.plan === 'string' ? formData.plan : undefined,
      });
    },
  });
}

Concrete example: Amplitude + Adjust

The example below sends:
  • Product analytics events to Amplitude
  • Attribution / marketing events to Adjust
For Expo, use a development build or prebuild setup for native SDKs such as Adjust.

React Native / Expo (development build)

npm install @amplitude/analytics-react-native react-native-adjust
import { Flowboard } from 'flowboard-react';
import { init as amplitudeInit, track as amplitudeTrack } from '@amplitude/analytics-react-native';
import { Adjust, AdjustConfig, AdjustEnvironment, AdjustEvent } from 'react-native-adjust';

const ADJUST_STEP_TOKEN = 'abc123';
const ADJUST_COMPLETE_TOKEN = 'xyz789';

export function initTracking() {
  amplitudeInit('AMPLITUDE_API_KEY');

  const config = new AdjustConfig('ADJUST_APP_TOKEN', AdjustEnvironment.Production);
  Adjust.create(config);
}

function trackAdjustStep(stepId: string, pageStep: number) {
  const event = new AdjustEvent(ADJUST_STEP_TOKEN);
  event.addCallbackParameter('step_id', stepId);
  event.addCallbackParameter('page_step', String(pageStep));
  Adjust.trackEvent(event);
}

function trackAdjustComplete(plan: string | undefined) {
  const event = new AdjustEvent(ADJUST_COMPLETE_TOKEN);
  if (plan) event.addCallbackParameter('plan', plan);
  Adjust.trackEvent(event);
}

export async function launchTrackedFlow() {
  await Flowboard.launchOnboarding({
    onStepChange: (stepId, pageStep, formData) => {
      const plan = typeof formData.plan === 'string' ? formData.plan : undefined;

      amplitudeTrack('flow_step_viewed', {
        step_id: stepId,
        page_step: pageStep,
        plan,
      });

      if (typeof stepId === 'string' && stepId.length > 0) {
        trackAdjustStep(stepId, pageStep);
      }
    },
    onOnboardEnd: (formData) => {
      const plan = typeof formData.plan === 'string' ? formData.plan : undefined;

      amplitudeTrack('flow_completed', {
        plan,
        accepted_terms: formData.accept_terms === true,
      });

      trackAdjustComplete(plan);
    },
  });
}

Flutter

flutter pub add amplitude_flutter adjust_sdk
import 'package:amplitude_flutter/amplitude.dart';
import 'package:adjust_sdk/adjust.dart';
import 'package:adjust_sdk/adjust_config.dart';
import 'package:adjust_sdk/adjust_event.dart';
import 'package:flutter/material.dart';
import 'package:flowboard_flutter/flowboard_flutter.dart';

const String adjustStepToken = 'abc123';
const String adjustCompleteToken = 'xyz789';

final Amplitude amplitude = Amplitude.getInstance();

void initTracking() {
  amplitude.init('AMPLITUDE_API_KEY');

  final config = AdjustConfig('ADJUST_APP_TOKEN', AdjustEnvironment.production);
  Adjust.initSdk(config);
}

void trackAdjustStep(String stepId, int pageStep) {
  final event = AdjustEvent(adjustStepToken);
  event.addCallbackParameter('step_id', stepId);
  event.addCallbackParameter('page_step', pageStep.toString());
  Adjust.trackEvent(event);
}

void trackAdjustComplete(String? plan) {
  final event = AdjustEvent(adjustCompleteToken);
  if (plan != null && plan.isNotEmpty) {
    event.addCallbackParameter('plan', plan);
  }
  Adjust.trackEvent(event);
}

Future<void> launchTrackedFlow(BuildContext context) async {
  await Flowboard.launchOnboarding(
    context,
    onStepChange: (stepId, pageStep, formData) {
      final plan = formData['plan'] as String?;

      amplitude.logEvent('flow_step_viewed', eventProperties: {
        'step_id': stepId,
        'page_step': pageStep,
        'plan': plan,
      });

      if (stepId.isNotEmpty) {
        trackAdjustStep(stepId, pageStep);
      }
    },
    onOnboardEnd: (formData) {
      final plan = formData['plan'] as String?;

      amplitude.logEvent('flow_completed', eventProperties: {
        'plan': plan,
        'accepted_terms': formData['accept_terms'] == true,
      });

      trackAdjustComplete(plan);
    },
  );
}

Tracking recommendations

  • Avoid sending raw PII to ad attribution tools unless your policy allows it.
  • Keep event names stable (flow_step_viewed, flow_completed).
  • Forward only the fields your downstream tools actually use.
  • Version your event schema if you change formData keys.