insightdive_sdk 0.7.0 copy "insightdive_sdk: ^0.7.0" to clipboard
insightdive_sdk: ^0.7.0 copied to clipboard

Flutter SDK for Insightdive in-app surveys. Modal bottom-sheet or inline widget — embed anywhere, stream lifecycle events, zero user identifiers.

insightdive_sdk #

Drop-in Flutter SDK for Insightdive — the in-app feedback platform. Two embedding modes: a managed modal bottom sheet, or a plain widget you place anywhere in your tree. Streams lifecycle events back to your app. Fully anonymous — no user identifiers ever leave the device.

Install #

dependencies:
  insightdive_sdk: ^0.4.0

Quick start — modal bottom sheet #

The SDK manages the sheet lifecycle: it opens on show() and closes automatically after the user submits (or dismisses).

import 'package:insightdive_sdk/insightdive_sdk.dart';

void main() {
  Insightdive.configure(
    tenant: 'acme',                // your workspace slug
    survey: 'onboarding',          // the project slug
    apiKey: 'ik_abc123...',    // copy from Admin → Settings → API
  );
  runApp(const MyApp());
}

// anywhere a BuildContext is available:
final result = await Insightdive.show(context);
if (result.status == FeedbackStatus.completed) {
  // user submitted
}

Check availability first #

Before showing a survey — whether via modal or inline widget — call isAvailable() to confirm the project has an active published deployment. Any network error or disabled project returns false, keeping your UI clean:

@override
void initState() {
  super.initState();
  Insightdive.isAvailable().then((active) {
    if (active) setState(() => _showSurvey = true);
  });
}

isAvailable() hits GET /api/v1/surveys/{survey}/status — a public, auth-free endpoint cached 30 s server-side. No credentials needed.

Inline widget — embed anywhere #

Use InsightdiveSurvey when you want full control over placement and lifecycle: inside a full-screen page, a tab body, a card in a ListView, or any layout you build.

import 'package:insightdive_sdk/insightdive_sdk.dart';

class MySurveyPage extends StatefulWidget {
  const MySurveyPage({super.key});
  @override
  State<MySurveyPage> createState() => _MySurveyPageState();
}

class _MySurveyPageState extends State<MySurveyPage> {
  bool _showSurvey = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _showSurvey
          ? InsightdiveSurvey(
              options: Insightdive.options,
              onEvent: (event) {
                if (event is FeedbackCompleted) {
                  setState(() => _showSurvey = false);
                }
              },
            )
          : const Center(child: Text('Thanks for your feedback!')),
    );
  }
}

The widget does not auto-dismiss — you decide when to remove it from the tree by reacting to FeedbackCompleted (or any other event) in onEvent.

To also populate the global Insightdive.events stream when using the inline widget, forward events manually:

InsightdiveSurvey(
  options: Insightdive.options,
  onEvent: (e) => Insightdive.addEvent(e),
)

Configuration #

Field Type Description
tenant String Workspace slug — the subdomain on insightdive.com (e.g. 'acme').
survey String Project slug from the admin (e.g. 'onboarding'). Always serves the currently active deployment.
apiKey String Tenant API key found in Admin → Settings → API. Required — the survey returns an access denied page without it.
baseUrl String? Full URL override for staging or self-hosted instances (e.g. 'http://acme.localhost:3000').
productVersion String? Free-form version stamped on every submission (e.g. '1.2.3').
productIdentifier String? Surface slug — useful when one app embeds the SDK in several places.
locale String? Locale to render the survey in. Falls back to the survey's default.
theme String? 'light' or 'dark'. Falls back to the survey's authored theme.
context Map<String, dynamic>? Operator-defined key/value metadata stamped on every submission (plan, feature flags, surface…). See Operator context.
respondentToken String? Opaque hash set by the operator for cross-referencing responses without storing a user identity. See Respondent token.

The SDK loads https://<tenant>.insightdive.com/s/<survey>?apiKey=... by default. Rotating the survey from the admin doesn't require a new app build — the /s/... route always resolves to whichever deployment is active.

Lifecycle events #

Both the modal and inline widget emit the same events. Subscribe globally via Insightdive.events, or locally via the onEvent callback on InsightdiveSurvey:

Insightdive.events.listen((event) {
  switch (event) {
    case FeedbackReady():      // page loaded, bridge alive
    case FeedbackViewed():     // SurveySession registered as `viewed`
    case FeedbackStarted():    // user clicked Start
    case FeedbackCompleted(:final submissionId):
      analytics.track('feedback_completed', {'id': submissionId});
    case FeedbackDismissed():
      analytics.track('feedback_dismissed');
  }
});

The same identifiers (sessionId, submissionId) are also returned by the Insightdive.show(context) Future for the modal mode.

Event-based triggering #

trigger() calls the status endpoint with ?event=<name> before opening the survey. The survey only opens when the insight's trigger-event list (configured in the admin) includes the event name. When the list is empty every event matches.

// Fires the survey only when 'app_launched' is in the insight's trigger list.
if (mounted) await Insightdive.trigger(context, 'app_launched');

On network error the SDK falls through and opens the survey anyway (fail-open), so a transient connectivity blip never permanently hides the entry point.

Frequency capping (cooldown) #

isAvailable() and trigger() both respect the cooldown configured on the insight (Admin → Insight → General → Cooldown). The SDK tracks the last shown timestamp in memory per tenant+survey pair. If the cooldown period hasn't elapsed the call resolves as false / dismissed without hitting the network again.

Because the cooldown is in-memory it resets on app restart — intentional for day-scale cooldowns where a fresh session should be treated as a new opportunity.

Operator context fields #

Stamp arbitrary application context on every submission — plan tier, feature flags, active surface, A/B cohort — without collecting any user identity. The admin can filter, segment, and run AI analysis per context dimension.

Insightdive.configure(
  tenant: 'acme',
  survey: 'onboarding',
  apiKey: 'ik_abc123...',
  context: {
    'plan': 'enterprise',
    'trial_days_remaining': 12,
    'feature_export_v2': true,
    'surface': 'settings',
  },
);

Constraints (enforced server-side; invalid entries are silently dropped):

  • Max 20 keys per object
  • Key format: [a-z0-9_], max 64 characters
  • Values: String, int, double, or bool — no nested objects
  • String values: max 255 characters
  • Total JSON payload: max 4 KB

Context values are visible in Admin → Responses (detail view) and included in CSV exports as context.<key> columns.


Respondent token #

Let the operator correlate responses with their own user records — without ever exposing a user identity to Insightdive. Compute an opaque hash client-side and pass it as respondentToken:

import 'package:crypto/crypto.dart';
import 'dart:convert';

String computeToken(String userId, String workspaceSalt) {
  final bytes = utf8.encode(userId + workspaceSalt);
  return sha256.convert(bytes).toString();
}

Insightdive.configure(
  tenant: 'acme',
  survey: 'onboarding',
  apiKey: 'ik_abc123...',
  respondentToken: computeToken(currentUser.id, 'my-secret-salt'),
);

What Insightdive stores: the hash only — no ability to reverse it to a user.
What the operator can do: compute the same hash from their own database and look up all responses for that hash in Admin → Responses.
GDPR note: Insightdive is a processor with no identification capability. The operator (controller) owns the correlation.

Tip: rotate workspaceSalt if you want to "forget" historical correlations without deleting data.

Constraints: max 128 characters, characters [a-zA-Z0-9-_] only.


Screenshot capture #

When an insight has screenshot collection enabled (Admin → Insight → Settings → Delivery), the SDK can attach a screenshot of your app screen to the response. The screenshot is of the UI — never the user.

To enable it, wrap your app root in a RepaintBoundary keyed with Insightdive.screenshotBoundaryKey:

RepaintBoundary(
  key: Insightdive.screenshotBoundaryKey,
  child: MaterialApp(/* … */),
);

The SDK captures this boundary just before the sheet opens (so the sheet itself isn't in the shot) and forwards it to the survey. If the boundary isn't mounted, capture is skipped silently — nothing breaks. The flag is read from the status endpoint, so call isAvailable() / trigger() before show().

Programmatic control (modal only) #

// Close the sheet from code (e.g. on logout)
Insightdive.hide(context);

For the inline widget, simply remove it from your widget tree instead.

Platform support #

Platform Status
iOS ✅ Supported
Android ✅ Supported
macOS ✅ Supported
Windows ✅ Supported
Web ⚠️ Untested — webview_flutter on Flutter web requires extra setup
Linux ⚠️ Untested

Privacy #

The SDK does not collect or transmit any user-identifying data. Only the deploymentId, optional productVersion, optional productIdentifier, locale, and theme fields are forwarded to the server. Submissions are linked back to a SurveySession row server-side via an opaque id; that id never leaves the device tied to anything personal.

The optional context map contains only operator-stamped metadata — no PII. The optional respondentToken is an opaque hash the operator computes themselves; Insightdive never decodes it.

License #

MIT. See LICENSE.

0
likes
160
points
413
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for Insightdive in-app surveys. Modal bottom-sheet or inline widget — embed anywhere, stream lifecycle events, zero user identifiers.

Homepage
Repository (GitHub)
View/report issues

Topics

#feedback #survey #nps #csat #webview

License

MIT (license)

Dependencies

flutter, webview_flutter

More

Packages that depend on insightdive_sdk