Skip to content

User Consent

Part of the Human Scoring use case.

The SDK records how the user interacts with your app, so by default it records nothing until the user agrees. The SDK ships a ready-made consent dialog, and you can replace it with your own UI without losing the guarantee.

When to use it

  • Always, unless your app already obtains consent before calling DeaSdk.init — in that case, set requireConsent: false
  • To change the wording of the dialog, or translate it
  • To let the user withdraw consent from a settings screen

Prerequisites

  • The SDK is installed and initialized. See the Quick Start.

Usage

Show the built-in dialog

import 'package:dea_sdk/dea_sdk.dart';

final agreed = await showDeaConsentDialog(context);
  • The answer is stored on the device. Once the user has answered, later calls return the stored answer without showing anything, so it is safe to call on every launch.
  • The dialog follows your app theme's platform: by default a Material dialog on Android and a Cupertino alert on iOS.
  • It cannot be dismissed by tapping outside. If it closes any other way (for example the back button), it returns false but records nothing, so the user is asked again next time.

Change the wording

await showDeaConsentDialog(
  context,
  copy: const ConsentCopy(
    title: 'Help us keep bots out',
    body: 'We measure how you interact with the app to tell people apart from bots.',
    recorded: ['Where you touch, and how hard', 'How the phone moves in your hand'],
    notRecorded: ['What you type', "What's on your screen"],
    closing: 'You can change this at any time in Settings.',
    accept: 'Allow',
    decline: 'Not now',
  ),
);

Pass an empty list to recorded or notRecorded to hide that section. Make sure the lists stay true for your app.

Choose the dialog style

await showDeaConsentDialog(context, style: ConsentDialogStyle.material); // or .cupertino, .adaptive

Use your own UI

Draw your own dialog or onboarding step, then record the answer:

await DeaSdk.instance?.setConsent(true);  // the user agreed
await DeaSdk.instance?.setConsent(false); // the user declined

Recording starts or stops as soon as setConsent is called; awaiting only waits for the answer to be stored.

Read the current answer

final consent = DeaSdk.instance!.consent;

consent.isAnswered; // false until the user has answered
consent.isGranted;  // true only if the user agreed
consent.decision;   // ConsentDecision.unknown, .granted or .denied
await DeaSdk.instance?.setConsent(false);

Recording stops immediately, and input the SDK was still holding back is discarded. The session is kept, so if the user agrees again, recording continues in the same session. What was scored before the withdrawal stays in the session's classification; call reset() as well to start again from nothing.

Ask again

await DeaSdk.instance?.consent.forget();

forget() clears the stored answer, so the next showDeaConsentDialog call asks again — for example when a different account signs in on the same device. Unlike declining, it is not an answer. With requireConsent on (the default), nothing is recorded until the user answers again.

Shared devices

When a different person may use the device each session (a kiosk, a test device), do not store the answer:

await showDeaConsentDialog(context, remember: false);

With remember: false any stored answer is cleared first, the dialog is shown on every call, and the new answer is not stored.

Examples

Ask on the first screen

import 'package:dea_sdk/dea_sdk.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) showDeaConsentDialog(context);
    });
  }

  @override
  Widget build(BuildContext context) => const Scaffold(body: Center(child: Text('Home')));
}

A settings switch

import 'package:dea_sdk/dea_sdk.dart';
import 'package:flutter/material.dart';

class DeaConsentTile extends StatefulWidget {
  const DeaConsentTile({super.key});

  @override
  State<DeaConsentTile> createState() => _DeaConsentTileState();
}

class _DeaConsentTileState extends State<DeaConsentTile> {
  @override
  Widget build(BuildContext context) {
    final dea = DeaSdk.instance;
    return SwitchListTile(
      title: const Text('Bot protection'),
      subtitle: const Text('Measure how I interact with the app'),
      value: dea?.consent.isGranted ?? false,
      onChanged: dea == null
          ? null
          : (value) async {
              await dea.setConsent(value);
              setState(() {});
            },
    );
  }
}

API

Member Returns Description
showDeaConsentDialog(context, {copy, style, remember, ask}) Future<bool> Shows the dialog if needed and returns the answer. ask: false only reads the stored answer
DeaSdk.setConsent(granted, {remember}) Future<void> Records the answer
DeaSdk.consent Consent The current answer
Consent.isAnswered bool Whether the user has answered
Consent.isGranted bool Whether the user agreed
Consent.decision ConsentDecision unknown, granted or denied
Consent.forget() Future<void> Clears the stored answer
Consent.addListener(listener) / removeListener(listener) void Be notified when the answer changes