Quick start: Flutter
Add the digid plugin, start it with your licence, open the ready-made flow and send the document to your backend.
The digid Flutter plugin runs on Android and iOS. It ships the engine and the ready-made screens: intro, card camera (front, then the back if you ask for it), selfie, fingerprints and result.
1. Add the plugin
Add digid to your app's pubspec.yaml. Customers get the package and its install line from the customer portal: see Get the SDK.
The plugin makes no network calls and declares no permissions. Your app asks for the camera:
- Android:
<uses-permission android:name="android.permission.CAMERA" />inAndroidManifest.xml. - iOS: an
NSCameraUsageDescriptionstring inInfo.plist.
If the user refuses the camera, the flow says so and offers to open the app's settings.
2. Start the engine with your licence
Create the engine once, at start-up. Loading takes a moment and runs off the UI isolate.
import 'package:digid/digid.dart';
const licence = 'digidlic1.…'; // your licence key, or fetch it from your backend
final engine = await DigidFlow.createEngine(licence: licence);
The licence is checked on the device, against your app's package name or bundle id. Without a valid licence the engine runs in evaluation mode. See Licences and renewal.
3. Open the flow
final result = await DigidFlow.start(
context,
engine: engine,
licence: licence,
config: DigidFlowConfig.fromJson({
'flow': {'steps': ['card', 'selfie'], 'require_back': 'optional'},
'screens': {'result': {'download': false}},
}),
onStepChanged: (stage) => debugPrint('digid: ${stage.wire}'),
onError: (error) => debugPrint('digid: $error'),
);
start returns null when the user leaves the flow. To embed the flow in your own page instead of pushing it, use the DigidFlowView widget with the same arguments. Every config key is in the config reference.
For fingerprints, add 'fingerprints' to the steps and pass a fingerprint engine: see Fingerprints for NIDA.
4. Read the result
if (result != null) {
final overall = result.kyc?.overall; // pass, review or fail
final evaluation = result.evaluation; // true without a valid licence
final document = result.payload; // the digid.kyc/1 document, as JSON text
}
result.kycis the engine's result (KycResult), typed.result.payloadis thedigid.kyc/1document. It isnullonly if it could not be built, which is reported throughonError.
5. Send the document to your backend
Send result.payload as it is, over your own authenticated connection. Do not parse and re-serialise it first: that can change the text and fail the tamper check on your server.
import 'package:http/http.dart' as http;
await http.post(
Uri.parse('https://api.example.co.tz/kyc'),
headers: {'content-type': 'application/json', 'authorization': 'Bearer $sessionToken'},
body: result.payload,
);
Then check it on the server: Verify on your backend.
6. Report usage
Under a licence the engine counts completed verifications on the phone: counts only, nothing about the person. Your app sends the daily reports when it is online. Pay-as-you-go licences also renew from the phone.
engine.usageSender = (report) async {
final r = await http.post(Uri.parse('https://digid.co.tz/api/v1/usage'),
headers: {'content-type': 'application/json'}, body: jsonEncode({'report': report}));
return r.statusCode == 200;
};
await engine.setUsageStore(myStore); // keeps the counts across restarts
await engine.online(); // call when the app is online, e.g. from a connectivity listener
See Licences and renewal for the store, renewal and the "no report, no renewal" rule.
Next
- Drop-in screens and config: theme, strings, steps and consent.
- Going live: the checklist before production.