handleAction method
Implementation
Future<void> handleAction(GuideAction action) async {
final state = _orchestrator.state;
if (state == null) return;
final firstAction = action.actions.isEmpty ? null : action.actions.first;
// Guides only have Step Clicked in the matrix (no Experience Clicked), so
// the click is Digia analytics only — never forwarded to the CEP plugin.
_events().toDigia(
GuideStepClicked(
itemIndex: _currentItemIndex,
ctaLabel: action.label,
actionType: action.analyticsType,
),
state.payload,
);
// A CTA tap (anything but back/prev) on the last or only step is the guide's
// "Completed" — the single place completion is decided. Scrim taps, the close
// button, the back gesture, and an outside-tap advance never reach here, so
// they stay plain dismissals. Mirrors RN's handleActionPress rule.
if (_isOnLastActiveStep &&
!_completedFired &&
firstAction is! PreviousAction) {
_completedFired = true;
// Permanent stop when the policy opted into stopOn: experienceCompleted.
_frequency().recordCompleted(state.campaign);
_events().toDigia(
GuideCompleted(
itemTotal: state.config.stepCount,
timeToCompleteMs: _dwell.elapsedMs(state.payload.cepCampaignId),
),
state.payload,
);
}
final scope = EngageActionScope(
dismiss: () => _view.dismiss(),
next: () => _transition(forward: true),
previous: () => _transition(forward: false),
openUri: (uri, {required external}) => launchUrl(
uri,
mode: external
? LaunchMode.externalApplication
: LaunchMode.platformDefault,
),
share: (_) async {},
copy: (_) async {},
requestReview: () async {},
);
await EngageActionRunner.shared.run(
action.actions,
scope,
EngageActionContext(
campaignId: state.campaign.id,
campaignKey: state.campaign.campaignKey,
surface: EngageSurface.guide,
),
VariableScope.fromSchemas(
state.campaign.config.defaultVariables,
state.payload.variables,
),
);
}