Usage
This guide describes how to integrate and operate the KLP Flutter SDK in your application: initialization, session management, the loyalty WebView, UI components, event handling, and error management.
Usage
This guide describes how to integrate and operate the KLP Flutter SDK in your
application: initialization, session management, the loyalty WebView, UI
components, event handling, and error management.
Integration lifecycle
The SDK expects methods to be called in a defined order. Calling an API before
initialization completes results in KlpNotInitializedException.
Application start
└─ initialize() once, at cold start
└─ setUser() after your app authenticates the user
├─ launch() present the loyalty WebView
├─ widgets entry button, reward banner
├─ logEvent() report business events
└─ event streams rewards, deep links, and more
└─ logout() when the user signs out of your app
Initialize the SDK
Call initialize() once after WidgetsFlutterBinding.ensureInitialized().
Environment
Use thetestenvironment for development and staging builds to avoid
unintended transactions on the production loyalty platform.
import 'package:flutter/foundation.dart';
import 'package:klp_flutter_sdk/klp_flutter_sdk.dart';
Future<void> bootstrapKlp() async {
try {
await Klp.instance.initialize(
KlpInitializeConfig(
environment: kReleaseMode ? KlpEnvironment.prod : KlpEnvironment.test,
debugMode: !kReleaseMode,
showDebugScreen: false,
),
);
} on KlpSecurityException catch (e) {
// The device did not pass mandatory integrity checks.
// Restrict access to sensitive loyalty features.
}
}Configuration options
| Field | Type | Description | |
|---|---|---|---|
environment | KlpEnvironment | Target environment: test, or prod | |
debugMode | bool | Enables verbose native logging. Set to false in production | |
showDebugScreen | bool | false | When true, shows a floating debug button that opens the SDK debug interface. Security checks remain active. Must be false in production |
During initialization, the SDK performs native device-integrity checks, including
detection of rooted or jailbroken devices, emulators, debugging tools, and
application tampering. If mandatory checks fail, KlpSecurityException is thrown
and the result property contains detailed probe information.
Debug tools (integration testing)
Set showDebugScreen: true during development to display a floating debug
button. Tapping the button opens the SDK debug interface, where you can inspect security results, API logs, and trigger test
actions. Security checks remain enabled — unlike debugMode, which disables them.
You can also open the debug interface programmatically:
await Klp.instance.openDebugScreen();Both options must remain disabled in production builds (showDebugScreen: false;
do not call openDebugScreen() in release).
Manage the user session
After your application authenticates the user, bind their session to the SDK:
await Klp.instance.setUser(userId, accessToken);| Parameter | Description |
|---|---|
userId | Stable identifier from your identity system |
accessToken | Session token forwarded to the loyalty platform |
When the user signs out of your application, end the loyalty session:
await Klp.instance.logout();Security
Do not log or persist the access token outside your existing session
management. The SDK forwards it to the native layer and does not store it in
Dart.
Loyalty WebView
The full loyalty experience is presented through a native modal WebView. Open it
programmatically with launch(). An optional route parameter navigates to a
specific section:
await Klp.instance.launch(); // default entry point
await Klp.instance.launch('rewards'); // specific routeThe loyalty interface itself is rendered natively — it is not embedded inside
your Flutter widget tree. Your application triggers presentation from Dart and
responds to lifecycle events on the surrounding screen.
Requirements
- The SDK must be initialized
- A user session should be established (per your loyalty configuration)
- On iOS, an active view controller must be available — call from a mounted screen
- On Android, a foreground
Activityis required
WebView lifecycle events
Klp.instance.onWebViewClosed.listen((_) {
// The user closed the loyalty experience
});
Klp.instance.onDeepLink.listen((link) {
if (isAllowedDeepLink(link)) {
// Navigate within your application
}
});Treat deep link payloads as untrusted input. Validate scheme, host, and
path against an allowlist before routing.
Opening the WebView securely
Verify device integrity before presenting the loyalty experience on sensitive
screens:
final result = await Klp.instance.performSecurityChecks();
if (result.allPassed) {
await Klp.instance.launch();
}UI components
Depending on your integration, you may work with Ready to use Flutter components that connect to Klp.instance for loyalty entry points and reward feedback in your application shell. These components are developed for your organization —
APIs, visual design, and behaviour are shaped around your brand and agreed
requirements, not shipped as a fixed universal module. Where included in your delivery, import them from the same package:
import 'package:klp_flutter_sdk/klp_flutter_sdk.dart';KlpLoyaltyButton
KlpLoyaltyButtonA ready-made entry button that opens the loyalty WebView props and style will be change depends on need:
KlpLoyaltyButton(
label: 'My Rewards',
route: 'rewards',
icon: Icons.card_giftcard,
onError: (error) => handleLaunchError(error),
)| Parameter | Default | Description |
|---|---|---|
label | 'My Rewards' | Button label text |
route | null | Optional route passed to launch() |
icon | Icons.card_giftcard | Leading icon |
onError | null | Callback invoked if the launch fails (e.g. SDK not initialized) |
The button uses KlpLoyaltyTheme.primaryButtonStyle for consistent styling with
the SDK design system.
KlpRewardBanner
KlpRewardBannerA stream-driven banner that surfaces the most recent reward. It renders nothing
until the first onRewardEarned event arrives:
KlpRewardBanner(
onTap: (reward) => Klp.instance.launch('rewards'),
)| Parameter | Description |
|---|---|
onTap | Optional callback when the banner is tapped; receives the KlpReward |
The banner displays the reward name, description, and point value. It listens
to Klp.instance.onRewardEarned internally — no manual stream wiring is required.
KlpLoyaltyTheme
KlpLoyaltyThemeDesign tokens for loyalty surfaces in your application:
| Token | Description |
|---|---|
primary | Primary brand color for actions |
primaryDark | Text and icon emphasis |
accent | Highlight color |
surface | Card and banner backgrounds |
cornerRadius | Shared border radius |
primaryButtonStyle | Pre-configured ButtonStyle for loyalty buttons |
Use these tokens when building custom loyalty UI alongside the provided widgets.
Complete screen example
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await bootstrapKlp();
runApp(const MyApp());
}class LoyaltySection extends StatelessWidget {
const LoyaltySection({super.key});
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
KlpRewardBanner(),
SizedBox(height: 12),
KlpLoyaltyButton(label: 'My Rewards'),
],
);
}
}Connect session management to your authentication flow:
// After login
await Klp.instance.setUser(session.userId, session.accessToken);
// On logout
await Klp.instance.logout();Custom widget integration
You can build your own entry points and notifications using the same underlying
APIs the SDK widgets use:
ElevatedButton(
onPressed: () => Klp.instance.launch(),
child: const Text('My Rewards'),
)StreamBuilder<KlpReward>(
stream: Klp.instance.onRewardEarned,
builder: (context, snapshot) {
final reward = snapshot.data;
if (reward == null) return const SizedBox.shrink();
return ListTile(
title: Text(reward.name),
subtitle: Text(reward.description),
trailing: Text('+${reward.value.toStringAsFixed(0)}'),
);
},
)Report engagement events
Send business events to the loyalty platform:
await Klp.instance.logEvent('MoneyTransfer', {
'amount': 500,
'currency': 'TRY',
});Include only data approved for transmission to the loyalty backend. Avoid
personal identifiable information and secrets in event attributes unless
explicitly authorized in your data processing agreement.
Theme and language
await Klp.instance.setTheme(KlpTheme.dark);
final appearance = await Klp.instance.getAppearance();
// appearance.theme, appearance.resolvedTheme, appearance.languageTag
await Klp.instance.setLanguage('tr-TR'); // pass null to use the system localeSubscribe to onThemeChanged and onLanguageChanged to reflect changes made
by the user within the loyalty experience.
Event streams
All event streams are broadcast streams backed by a single native channel.
Multiple listeners are supported. Cancel subscriptions when they are no longer
needed to prevent memory leaks.
class LoyaltyController {
LoyaltyController() {
_subscriptions.add(Klp.instance.onRewardEarned.listen(_onReward));
_subscriptions.add(Klp.instance.onDeepLink.listen(_onDeepLink));
_subscriptions.add(
Klp.instance.onError.listen((e) => handleSdkError(e.message)),
);
_subscriptions.add(Klp.instance.onSecurityError.listen(_onSecurityError));
}
final _subscriptions = <StreamSubscription<dynamic>>[];
void dispose() {
for (final subscription in _subscriptions) {
subscription.cancel();
}
_subscriptions.clear();
}
void _onReward(KlpReward reward) { /* ... */ }
void _onDeepLink(KlpDeepLink link) { /* ... */ }
void _onSecurityError(KlpSecurityCheckResult result) { /* ... */ }
}Available streams
| Stream | Payload | Description |
|---|---|---|
onEvent | KlpEventPayload | General events from the loyalty WebView |
onRewardEarned | KlpReward | User earned a reward |
onError | KlpErrorPayload | Recoverable SDK error |
onSecurityError | KlpSecurityCheckResult | Runtime security verification failure |
onSseEffects | List<KlpSseEffect> | Real-time gamification effects |
onDeepLink | KlpDeepLink | Navigation request from the loyalty WebView |
onWebViewClosed | void | User dismissed the loyalty WebView |
onThemeChanged | KlpThemeChanged | Theme changed within the experience |
onLanguageChanged | KlpLanguageChanged | Language changed within the experience |
Payload reference
KlpReward
| Field | Type | Description |
|---|---|---|
id | String | Reward identifier |
name | String | Display name |
description | String | Description text |
imageUrl | String | Image URL |
value | double | Point value |
type | String | Reward category |
earnedAt | int | Timestamp in milliseconds |
KlpDeepLink
| Field | Type | Description |
|---|---|---|
url | String | Full URL |
scheme | String? | URL scheme |
host | String? | Host name |
path | String? | Path |
queryParameters | Map<String, String>? | Query parameters |
KlpSecurityCheckResult
| Field | Type | Description |
|---|---|---|
allPassed | bool | Whether all checks passed |
checks | List<KlpSecurityCheck> | Individual probe results (name, passed, errorMessage) |
KlpEventPayload — type, data (map or string), timestamp (ms).
KlpSseEffect — gamification effects with id, ruleName, missionId,
levelId, actionType, and a typed detail object for point grants and
mission progress.
API reference
| Method | Returns | Description |
|---|---|---|
initialize(config) | Future<bool> | Initialize the SDK and run security checks |
setUser(userId, accessToken) | Future<bool> | Start a user session |
logout() | Future<bool> | End the user session |
launch([path]) | Future<bool> | Present the loyalty WebView |
logEvent(name, [attributes]) | Future<bool> | Send an engagement event |
setTheme(theme) | Future<bool> | Set theme: system, light, or dark |
getAppearance() | Future<KlpAppearance> | Read current theme and language |
setLanguage(languageTag) | Future<bool> | Set language; null restores system locale |
isInitialized() | Future<bool> | Whether initialization completed |
isDeviceSecure() | Future<bool> | Device integrity verdict |
performSecurityChecks() | Future<KlpSecurityCheckResult> | Detailed security probe results |
openDebugScreen() | Future<bool> | Open the SDK debug interface programmatically |
Error handling
SDK errors are surfaced through a typed exception hierarchy:
try {
await Klp.instance.initialize(
KlpInitializeConfig(environment: KlpEnvironment.prod),
);
await Klp.instance.launch();
} on KlpSecurityException catch (e) {
for (final check in e.result?.checks ?? const []) {
if (!check.passed) {
logSecurityFailure(check.name, check.errorMessage);
}
}
} on KlpNotInitializedException {
// A method was called before initialize() completed
} on KlpPlatformException catch (e) {
logPlatformError(e.code, e.message);
}| Exception | Error codes | Typical cause |
|---|---|---|
KlpSecurityException | KLP_INIT_SECURITY_ERROR, KLP_SECURITY_ERROR | Device integrity check failed |
KlpNotInitializedException | KLP_NOT_INITIALIZED | SDK used before initialization |
KlpPlatformException | KLP_LAUNCH_ERROR, KLP_SET_USER_ERROR, and others | Native bridge operation failed |
Security guidelines
- Set
debugMode: falseandshowDebugScreen: falsein production openDebugScreen()andshowDebugScreenare intended for integration testing
builds only — never enable them in production releases- Validate all deep links before acting on them
- Gate sensitive flows on
performSecurityChecks()before callinglaunch()
Release readiness
Before shipping to production, confirm the following:
-
initialize()is called once before any other SDK method -
environmentis set toprodin release builds -
debugModeandshowDebugScreenarefalsein release builds - Event stream subscriptions are cancelled when no longer needed
-
setUserandlogoutare connected to your authentication lifecycle - Deep links are validated before navigation
- Sensitive flows are gated on
performSecurityChecks() - The SDK version is pinned to a release tag in
pubspec.yaml
Updated 29 days ago
