redacto_consent_sdk 2.0.0
redacto_consent_sdk: ^2.0.0 copied to clipboard
Flutter SDK for integrating Redacto consent notices.
redacto_consent_sdk #
Flutter SDK for integrating Redacto consent notices and a self-service Privacy Center into your app.
Installation #
flutter pub add redacto_consent_sdk
Requirements #
- Dart
>=3.3.0 <4.0.0 - Flutter
>=3.19.0
Architecture #
The SDK follows a client-server architecture pattern:
- Your Backend Server acts as a proxy between your Flutter application and Redacto's API, handling token generation and refresh.
- Your Flutter Application uses SDK widgets to display consent notices and collect user choices.
- SDK Widgets fetch consent content, render UI, and submit consent records.
Before You Start #
You need three UUIDs from your Redacto Console and a CMS API Key.
Where to Find Your UUIDs #
| Value | Where to find it |
|---|---|
| Organisation UUID | Console → Settings → Organization → Copy UUID (or from the URL: /org/{ORG_UUID}/...) |
| Workspace UUID | Console → Settings → Workspaces → Select your workspace → Copy UUID (or from the URL: /org/.../workspace/{WORKSPACE_UUID}/...) |
| Notice UUID | Console → Consent Management → Notices → Click on your notice → Copy UUID (or from the URL: .../notices/{NOTICE_UUID}) |
CMS API Key #
Your backend needs a CMS API Key to call the Redacto token API. This key must never appear in frontend code.
Important: You need to obtain your CMS API Key from the Redacto Dashboard. Store this key securely in your environment variables and never expose it to the frontend.
- Go to Console → Settings → API Keys.
- Click Create API Key.
- Copy the key — you will not see it again.
- Store it as an environment variable on your server:
export CMS_API_KEY="your-api-key-here"
Required App Configuration #
Store the following values in your app configuration (for example, Dart defines, secure config, or remote config):
| Config Value | Required | Used For |
|---|---|---|
BACKEND_URL |
Yes | Your backend endpoint for consent token and refresh token APIs |
NOTICE_UUID |
Yes (modal flows) | Notice rendered by RedactoNoticeConsent |
ORGANISATION_UUID |
Yes (inline flows) | Loading consent content and token requests |
WORKSPACE_UUID |
Yes (inline flows) | Loading consent content and token requests |
BASE_URL |
No | Override the default Redacto consent API base URL |
How to wire these values:
- Set these values in your app's runtime/build configuration.
- Send
ORGANISATION_UUID,WORKSPACE_UUID, and optionalBASE_URLto your backend token endpoint. - Use returned
accessTokenandrefreshTokenwithRedactoNoticeConsent, or pass UUIDs (and tokens when available) toRedactoNoticeConsentInline.
Components #
RedactoNoticeConsent #
Modal consent component for initial consent and reconsent flows.
Best for: Initial consent collection, standalone consent flows
Key Characteristics:
- Requires tokens upfront (must be provided before rendering)
- Displays as a modal overlay
- Blocks UI interaction by default
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
noticeId |
String |
Yes | - | Notice UUID |
accessToken |
String |
Yes | - | JWT access token |
refreshToken |
String |
Yes | - | JWT refresh token |
baseUrl |
String? |
No | https://api.redacto.io/consent |
Override consent API base URL (notice fetch, tokens, legacy writes) |
ledgerBaseUrl |
String? |
No | - | Go ledger base URL. When set, consent writes and prior-consent checks route through the ledger (falls back: ledger → baseUrl → default). Omit to keep the legacy consent-server path. |
settings |
ConsentSettings? |
No | - | UI styling overrides |
language |
String |
No | "en" |
Initial language |
blockUI |
bool |
No | true |
Prevents back-dismiss |
onAccept |
VoidCallback |
Yes | - | Called on accept |
onDecline |
VoidCallback |
Yes | - | Called on decline |
onError |
ValueChanged<Object>? |
No | - | Error callback |
applicationId |
String? |
No | - | Application/user-specific UUID |
validateAgainst |
ValidateAgainst |
No | ValidateAgainst.all |
Validation mode |
includeFullyConsentedData |
bool |
No | false |
Include review payload for already-consented users |
reviewModeButtonText |
String? |
No | "Update Consent" UI path |
Custom review CTA text |
onPrivacyCenterTap |
VoidCallback? |
No | - | Called instead of opening the privacy center URL in an external browser; use for in-app Privacy Center navigation |
When onPrivacyCenterTap is set, tapping the privacy center link in the notice routes to your handler (for example, Navigator.push to a screen hosting RedactoPrivacyCenter). See Privacy Center.
RedactoNoticeConsentInline #
Embedded consent component for inline form contexts.
Best for: Registration forms, checkout flows, multi-step processes
Key Characteristics:
- Renders inline within your form
- Auto-submits consent when all conditions are met
Auth paths: The inline widget supports two mutually exclusive auth paths — provide either noticeId + accessToken (post-token) OR orgUuid + workspaceUuid + noticeUuid (pre-token: render the widget while you fetch tokens). Params marked No* in the table below are conditionally required: exactly one of the two combos must be provided.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
noticeId |
String? |
No* | - | Notice UUID (post-token path) |
accessToken |
String? |
No* | - | JWT access token (post-token path) |
orgUuid |
String? |
No* | - | Organisation UUID (pre-token path) |
workspaceUuid |
String? |
No* | - | Workspace UUID (pre-token path) |
noticeUuid |
String? |
No* | - | Notice UUID (pre-token path) |
baseUrl |
String? |
No | https://api.redacto.io/consent |
Override consent API base URL (notice fetch, legacy writes) |
ledgerBaseUrl |
String? |
No | - | Go ledger base URL. When set, consent writes and already-consented checks route through the ledger. Applies to the post-token path only (both require an accessToken). Omit for legacy behavior. |
settings |
ConsentSettings? |
No | - | UI styling overrides |
language |
String |
No | "en" |
Initial language |
onAccept |
VoidCallback? |
No | - | Called after auto-submit |
onDecline |
VoidCallback? |
No | - | Decline callback |
onError |
ValueChanged<Object>? |
No | - | Error callback |
onValidationChange |
ValueChanged<bool>? |
No | - | Emits validation state |
applicationId |
String? |
No | - | Application/user-specific UUID |
validateAgainst |
ValidateAgainst |
No | ValidateAgainst.required |
Validation mode |
includeFullyConsentedData |
bool |
No | false |
Include review payload for already-consented users |
Usage Example #
import "package:flutter/material.dart";
import "package:redacto_consent_sdk/redacto_consent_sdk.dart";
class ConsentDemo extends StatelessWidget {
const ConsentDemo({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (_) => RedactoNoticeConsent(
noticeId: NOTICE_UUID,
accessToken: accessToken,
refreshToken: refreshToken,
validateAgainst: ValidateAgainst.all,
onAccept: () => Navigator.of(context).pop(),
onDecline: () => Navigator.of(context).pop(),
onError: (error) => debugPrint(error.toString()),
),
);
},
child: const Text("Open consent"),
);
}
}
Styling (ConsentSettings) #
ConsentSettings supports:
- Base properties:
link,borderRadius,backgroundColor,headingColor,textColor,borderColor,font - Buttons:
button.accept(backgroundColor,textColor)button.decline(backgroundColor,textColor)button.language(backgroundColor,textColor,selectedBackgroundColor,selectedTextColor)
Privacy Center #
A self-service Privacy Center widget for managing consents, submitting data subject access requests (DSAR), and tracking activity. Renders as a mobile-first single column (matching React Native): top navbar (page title, email, language selector, theme toggle), page content, bottom tab bar (Consents | Requests | Activities | Receipts), and a "Powered by Redacto" footer. Case details is an internal overlay reached from the Requests tab—not a bottom-tab page.
baseUrl is the consent API base URL (same as notice flows, for example https://api.redacto.io/consent), not a separate privacy center URL.
Import #
import "package:redacto_consent_sdk/redacto_consent_sdk.dart";
RedactoPrivacyCenter, PrivacyCenterPage, and related Privacy Center exports are available from the main library entry—no separate import path (unlike React Native's /privacy-center subpath).
Dependencies #
Privacy Center features rely on packages bundled as direct SDK dependencies: intl, file_picker, share_plus, open_filex, and path_provider. Receipt download/share and DSAR file upload use the platform share sheet and file picker; follow standard Flutter iOS and Android setup for those plugins. You do not need to add these packages separately when using redacto_consent_sdk.
RedactoPrivacyCenter #
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
baseUrl |
String |
Yes | - | Consent API base URL for Privacy Center requests |
ledgerBaseUrl |
String? |
No | - | Go ledger base URL. When set, user consents, receipts, and manage-consent route through the ledger. Omit to use the legacy /dsar/privacy-center/* routes. See the note below on the DSAR form picker. |
accessToken |
String |
Yes | - | JWT access token |
refreshToken |
String |
Yes | - | JWT refresh token |
onError |
void Function(Object error) |
Yes | - | Error handler |
theme |
Brightness |
No | Brightness.light |
Visual theme (Brightness.light or Brightness.dark); user can toggle from the navbar |
initialPage |
PrivacyCenterPage |
No | PrivacyCenterPage.consentManager |
Initial tab: consentManager, form, activity, or receipt. Do not use caseDetails—it is internal and opened from the Requests list |
language |
String |
No | "en" |
Initial language code (for example en, hi); drives UI strings and the API language query param; user can change from the navbar |
contactEmail |
String? |
No | - | User contact email for case history and forms when the JWT does not embed it (common with backend-issued consent tokens) |
onBack |
VoidCallback? |
No | - | When set, the navbar shows a back arrow that invokes this callback (for example Navigator.of(context).pop()) |
Usage Example #
import "package:flutter/material.dart";
import "package:redacto_consent_sdk/redacto_consent_sdk.dart";
class PrivacyCenterScreen extends StatelessWidget {
const PrivacyCenterScreen({
super.key,
required this.accessToken,
required this.refreshToken,
this.userEmail,
});
final String accessToken;
final String refreshToken;
final String? userEmail;
@override
Widget build(BuildContext context) {
return Scaffold(
body: RedactoPrivacyCenter(
baseUrl: BASE_URL,
accessToken: accessToken,
refreshToken: refreshToken,
contactEmail: userEmail,
theme: Brightness.light,
initialPage: PrivacyCenterPage.consentManager,
onBack: () => Navigator.of(context).pop(),
onError: (error) => debugPrint(error.toString()),
),
);
}
}
Ledger mode #
Passing ledgerBaseUrl routes consent-bearing reads and writes to the Go ledger:
- Notice widgets —
submit-consentgoes to the ledger, and the already-consented check uses the ledger'scheck-consentendpoint instead of the legacy HTTP 409 signal. Notice content and tokens always stay onbaseUrl. - Privacy Center — user consents, receipts (including the
/downloadPDF path), and manage-consent (sent as apartial: truewrite) route to the ledger. DSAR form, cases, activities, uploads, and OTP stay onbaseUrl.
ledgerBaseUrl is independent of baseUrl — you still pass both. Every ledger read degrades to the legacy path on failure, so a ledger outage does not break the consent flow.
Known limitation. The DSAR form is not ledger-aware. It sources its purpose list from Python's
form/dataflatpurposesarray and has no product-grouped picker (the React and React Native SDKs group DSAR targets by product and, in ledger mode, source that picker from ledger-served consents — Flutter has no equivalent, andform/data'sdirect_groups/nominated_groupsare not parsed). SettingledgerBaseUrldoes not change the form. If your backend'sform/datastops returningpurposesfor a workspace cut over to the ledger, the form's purpose list will be empty.
Headless API #
For fully custom UIs, the package also exports headless building blocks on top of the same backend:
PrivacyCenterApiandPrivacyCenterAuthManager— API client and token refreshPrivacyCenterPage,PCScope,PcTheme— navigation scope and theming- Models from
privacy_center_models.dart(cases, consents, activities, receipts, etc.)
See the example app for a runnable integration.
Sandbox Mode (No Backend Required) #
Sandbox mode lets you build and test a full consent PoC — notice → Privacy Center → DSAR — straight from your app, with no backend and no token minting. You paste a static sandbox token into the widget instead of your server minting a JWT. Everything is recorded as test data, isolated from live records. Going live is a config swap, not a rewrite.
Sandbox is available on the modal RedactoNoticeConsent and on RedactoPrivacyCenter. The inline RedactoNoticeConsentInline widget has no sandbox mode.
1. Get your sandbox credentials #
Redacto Dashboard → API Keys → Sandbox tab: copy the Sandbox token (static, never expires). Copy your Organization ID and Workspace ID from the same page under Workspace identifiers.
2. Collect consent #
Pass the sandbox token plus org/workspace and a test identity to RedactoNoticeConsent — no accessToken, no refreshToken:
import "package:flutter/material.dart";
import "package:redacto_consent_sdk/redacto_consent_sdk.dart";
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (_) => RedactoNoticeConsent(
noticeId: NOTICE_UUID,
token: SANDBOX_TOKEN,
organisationUuid: ORGANISATION_UUID,
workspaceUuid: WORKSPACE_UUID,
email: "test.user@example.com", // or mobile / ucic
onAccept: () => Navigator.of(context).pop(),
onDecline: () => Navigator.of(context).pop(),
onError: (error) => debugPrint(error.toString()),
),
);
3. Manage consent & raise a DSAR #
Give RedactoPrivacyCenter the same token, org, workspace, and test identity. The same test user can review their consent, withdraw it, and file a DSAR — all recorded as test data:
import "package:flutter/material.dart";
import "package:redacto_consent_sdk/redacto_consent_sdk.dart";
Scaffold(
body: RedactoPrivacyCenter(
baseUrl: BASE_URL,
token: SANDBOX_TOKEN,
organisationUuid: ORGANISATION_UUID,
workspaceUuid: WORKSPACE_UUID,
email: "test.user@example.com", // or mobile / ucic — same identity as step 2
onBack: () => Navigator.of(context).pop(),
onError: (error) => debugPrint(error.toString()),
),
);
Sandbox props #
These are added to both RedactoNoticeConsent and RedactoPrivacyCenter. In sandbox mode, accessToken and refreshToken are omitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
String? |
Yes | Static sandbox token. Its presence activates sandbox mode. |
organisationUuid |
String? |
Yes | Organization ID (replaces the JWT-decoded org in sandbox mode). |
workspaceUuid |
String? |
Yes | Workspace ID (replaces the JWT-decoded workspace in sandbox mode). |
email |
String? |
One of email/mobile/ucic | Test subject email. |
mobile |
String? |
One of email/mobile/ucic | Test subject mobile (used when email is absent). |
ucic |
String? |
One of email/mobile/ucic | Client's own user id (org_user_id). Takes precedence: ucic > email > mobile. |
How it works #
- The sandbox token is sent as the single
X-Consent-Tokenheader; its presence marks the request as test data. There is noAuthorization: Bearerand no JWT refresh, so a401fails fast instead of retrying. - The acting identity rides each request's own payload — query params (
org_user_id/primary_email/primary_mobile) on reads, the JSON body on writes — resolved in precedence orderucic>email>mobile. - The server records everything under
environment=test, namespaced and isolated. Test data never mixes with, or reads from, your live records. - Sandbox always talks to the consent server; it never routes to the Go ledger, so
ledgerBaseUrlhas no effect in sandbox mode.
Going live #
Use the same widgets. Remove the sandbox props (token, organisationUuid, workspaceUuid, and the test identity) and pass a real accessToken / refreshToken JWT minted by your backend. Records then write as environment=live.
Localization #
Notice consent #
- Set initial locale with
languageonRedactoNoticeConsentorRedactoNoticeConsentInline. - UI labels and text content are loaded from notice translations.
- Users can switch languages via the built-in selector when translations are available.
Privacy Center #
- Set initial locale with
languageonRedactoPrivacyCenter. - Privacy Center ships built-in UI strings for many locales; users switch language from the navbar selector.
languagedrives both UI strings and API-backed labels where applicable (request types, statuses, purposes).- Date and relative-time formatting uses
intlinternally; the SDK initializes locale data as needed—no extra app setup required.
Troubleshooting #
- No modal data: verify
noticeId,accessToken, andrefreshToken. - Inline submit not firing: ensure required selections are complete and
onValidationChangebecomestrue. - HTTP errors: check backend token generation and
baseUrlvalue. - Empty case history or case API errors: pass
contactEmailwhen your JWT does not include the user's email. - Wrong language on case details after toggle: the SDK refetches case data on language change; verify tokens are valid and
baseUrlis correct. - Receipt download or export failures: check platform file and share permissions (iOS share sheet, Android storage where applicable).
Development #
cd packages/consent-sdk-flutter
flutter pub get
dart analyze
flutter test