API reference & changelog
optolink_flutter 2.0.0 exports one entry point (OptoLink) and its config, data, and error types from package:optolink_flutter/optolink_flutter.dart. The public Dart API is source-compatible with 1.x; setup lives in install & initialize.
// ---------- entry point ----------
class OptoLink {
/// The one throwing API: config errors (native `invalid_argument`,
/// e.g. a malformed baseUrl) and any native init failure — including
/// a missing native plugin — surface as a thrown OptoLinkError.
/// A failed call resets the singleton, so retry is allowed. Repeat
/// calls after success return the same instance.
static Future<OptoLink> initialize(OptoLinkConfig config);
/// StateError before initialize — programming error.
static OptoLink get instance;
/// The most recent channel failure, or null when the last call
/// succeeded. Set on every rejected call; cleared on the next success.
static OptoLinkError? get lastError;
/// Stable per-install device ID (minted natively); null before initialize.
String? get deviceId;
OptoLinkConfig get config;
/// Deferred + warm direct links. Cold-start direct links NEVER appear
/// here (see the delivery rule on handle deep links). The deferred
/// link resolved inside initialize replays to the first subscriber.
/// Malformed payloads are logged at warning and dropped — the stream
/// never throws.
Stream<OptoLinkData> get onLink;
/// The cold-start direct link, natively resolved. Idempotent. Null when
/// the app was not deep-link-launched — or on channel failure, with
/// lastError set.
Future<OptoLinkData?> getInitialLink();
/// Manual re-attempt of the deferred flow (the automatic run happens
/// natively on first launch). Null on no-match or failure + lastError.
Future<OptoLinkData?> resolveDeferredLink();
// ---- identity & events: Future<bool> is channel success; false only
// on native error (details in lastError) or a missing plugin. ----
Future<bool> setIdentity(String externalId);
Future<bool> clearIdentity();
/// Property values coerced to strings at the channel edge (the natives
/// are String→String); null values dropped; each change logged at info.
Future<bool> trackEvent(String eventName, {Map<String, dynamic>? properties});
/// Resets the Dart client only — native state is process-owned.
void dispose();
}
What each call returns
| Call | Success | Failure |
|---|---|---|
initialize(config) | Instance returned, deviceId set | Throws OptoLinkError (bad config, native init failure, missing plugin); singleton reset so retry works |
getInitialLink() / resolveDeferredLink() | OptoLinkData? | null + lastError set |
setIdentity / clearIdentity / trackEvent | true | false + lastError set |
onLink | one stream, replay-last | never throws; malformed payloads logged and dropped |
Config
class OptoLinkConfig {
final String apiKey; // CLIENT-tier opl_sdk_… key from the portal
final String orgKey; // the /orgKey/ segment of your short URLs
final String baseUrl; // your OptoLink origin — serves your short links and /match
final Duration timeout; // default 10 s, per HTTP attempt
final int maxRetries; // default 2, native exponential backoff
final bool clipboardEnabled; // default true; false skips the clipboard leg
final OptoLinkLogger? logger; // default null
}
typedef OptoLinkLogger = void Function(
OptoLinkLogLevel level, String message, {Object? error});
enum OptoLinkLogLevel { info, warning, error }
baseUrl is a String, not a Uri: the native side validates it, and a malformed value makes initialize throw.
OptoLinkData
class OptoLinkData {
final String? linkId; // null for direct links not resolved server-side
final String path; // e.g. /promo/flash-24h
final Map<String, dynamic>? params;
final String matchType; // raw wire string, values below
final MatchConfidence confidence; // exact | high | medium | low
final int? score; // FINGERPRINT_SCORED matches only, 0–100
final bool isDeferred;
}
enum MatchConfidence { exact, high, medium, low }
matchType values
| Value | Meaning | confidence | score |
|---|---|---|---|
DIRECT | Universal Link / App Link open | exact | null |
CLIPBOARD | Clipboard token on first launch | exact | null |
INSTALL_REFERRER | Play Install Referrer (Android only) | exact | null |
FINGERPRINT_EXACT | All device attributes matched exactly | high | null |
FINGERPRINT_SCORED | Weighted scoring across attributes | high / medium / low | 0–100 |
IP_FUZZY | Same IP only | low | null |
matchType carries the native wire string verbatim ('unknown' if a payload omits it). An unrecognized confidence value falls back to medium.
OptoLinkError
Surfaced via OptoLink.lastError and thrown by initialize; nothing else throws.
class OptoLinkError {
final int? status; // HTTP status when server-side
final String message;
final String? requestId; // support correlation
final int? retryAfterSeconds; // when throttled
final List<Map<dynamic, dynamic>>? details; // 400 validation entries
final bool isConnectionError; // timeout / unreachable host
}
Logging
| Level | Meaning |
|---|---|
info | Progress trail: init summary (key redacted), per-rung deferred decisions, HTTP request lines, link emissions |
warning | Transient failure; the native SDK will retry |
error | Terminal failure; the SDK gave up |
With a logger wired, native onLog entries and Dart-edge events (coercions, failures) all reach it. Without one, entries go to debugPrint in debug builds only; production is silent unless a logger is wired.
Changelog
The plugin runs its own semver clock; a breaking change in the native SDKs or the backend forces a plugin major. The 2.0.0 behavioral deltas and their migration notes: Migrating from 1.x.
- 2.0.0 — converted to a thin channel client over the native OptoLink SDKs (Swift on iOS — resolved via SPM or vendored for CocoaPods apps; Kotlin via Maven Central). API source-compatible with 1.x. Added
OptoLinkData.score,OptoLink.lastError+OptoLinkError, theinfolog level. RemovedDeviceIdStoreand theOptoLinkDatafactories; cold-start direct links now arrive only viagetInitialLink(). New floors: Flutter 3.38.0, iOS 15.0, Android minSdk 23. Zero Dart dependencies. - 1.2.0 —
trackEvent(name, {properties})custom events, capped at 10 KiB serialized. - 1.1.0 —
deviceId,setIdentity/clearIdentity, automatic session registration on app open. - 1.0.0 — initial release: direct links, deferred pipeline, confidence levels, logger hook.