Skip to Content
SDK IntegrationDart / Flutter

Flutter SDK

Integrate TrustPin into your Flutter application for cross-platform certificate pinning on iOS, Android, and macOS.

Platform Support

PlatformMinimum VersionNative SDK
iOS13.0+TrustPin Swift SDK
AndroidAPI 21 (Android 5.0 Lollipop)TrustPin Kotlin SDK
macOS13.0+TrustPin Swift SDK

Note: While the underlying Android SDK supports API 21+, Flutter itself requires API 21 (Android 5.0) as a minimum. For production apps, we recommend targeting API 23+ (Android 6.0 Marshmallow) or higher for optimal security and performance.


Installation

Add TrustPin to your pubspec.yaml:

dependencies: trustpin_sdk: ^2.0.0

Then install:

flutter pub get

Git (Development)

For the latest development version:

dependencies: trustpin_sdk: git: url: https://github.com/trustpin-cloud/trustpin-libraries.git path: flutter/trustpin_sdk

Platform Setup

iOS Requirements

  • Minimum iOS Version: 13.0+
  • Xcode: 15.0+
  • Swift: 5.0+

No additional configuration required - the native SDK is linked automatically via CocoaPods.

Android Requirements

  • Minimum SDK: API 25 (Android 5.0+)
  • Target SDK: API 34+ (recommended)
  • Kotlin: 1.9.0+

The Android implementation uses the native TrustPin Kotlin SDK, automatically configured via Gradle.

macOS Requirements

  • Minimum macOS Version: 13.0+
  • Xcode: 15.0+
  • Swift: 5.0+

For sandboxed apps, add network entitlement to DebugProfile.entitlements and Release.entitlements:

<key>com.apple.security.network.client</key> <true/>

Quick Start

1. Get Your Credentials

Sign in to the TrustPin Dashboard  and retrieve:

  • Organization ID
  • Project ID
  • Public Key (Base64-encoded ECDSA P-256)

2. Initialize TrustPin

Add initialization to your app’s main entry point:

import 'package:flutter/material.dart'; import 'package:trustpin_sdk/trustpin_sdk.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize TrustPin try { await TrustPin.setLogLevel(TrustPinLogLevel.debug); await TrustPin.setup( organizationId: 'your-org-id', projectId: 'your-project-id', publicKey: 'your-base64-public-key', mode: TrustPinMode.strict, ); TrustPin initialized successfully'); } catch (e) { print('TrustPin initialization failed: $e'); } runApp(MyApp()); }

3. Verify Certificates

Future<void> verifyServerCertificate() async { const pemCertificate = ''' -----BEGIN CERTIFICATE----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Q1jx8... -----END CERTIFICATE----- '''; try { await TrustPin.verify('api.example.com', pemCertificate); Certificate is valid'); } on TrustPinException catch (e) { print('Verification failed: ${e.code} - ${e.message}'); if (e.isPinsMismatch) { print('Certificate doesn\'t match configured pins'); } else if (e.isDomainNotRegistered) { print('Domain not configured (strict mode)'); } } }

HTTP Client Integration

Using with Dio

The SDK provides a built-in TrustPinDioInterceptor:

import 'package:dio/dio.dart'; import 'package:trustpin_sdk/trustpin_sdk.dart'; // Create Dio with TrustPin certificate validation final dio = Dio(); dio.interceptors.add(TrustPinDioInterceptor()); // All HTTPS requests now have certificate pinning try { final response = await dio.get('https://api.example.com/data'); Request successful: ${response.statusCode}'); } on DioException catch (e) { if (e.error is TrustPinException) { final trustPinError = e.error as TrustPinException; print('Pinning failed: ${trustPinError.code}'); } } // Clear certificate cache if needed final interceptor = TrustPinDioInterceptor(); interceptor.clearCertificateCache();

Features:

  • Validates standard TLS certificates (OS-level)
  • Performs TrustPin certificate pinning validation
  • Caches certificates for performance
  • Prevents requests with invalid certificates

Using with http Package

The SDK provides TrustPinHttpClient that wraps the standard http.Client:

import 'package:http/http.dart' as http; import 'package:trustpin_sdk/trustpin_sdk.dart'; // Create TrustPin-enabled HTTP client final httpClient = TrustPinHttpClient.create(); // Or wrap an existing client final customClient = http.Client(); final httpClient = TrustPinHttpClient(customClient); // Use like a normal http.Client final response = await httpClient.get( Uri.parse('https://api.example.com/data') ); // Clear cache if needed httpClient.clearCertificateCache(); // Clean up when done httpClient.close();

Configuration

Pinning Modes

Strict Mode

Throws errors for unregistered domains:

await TrustPin.setup( organizationId: 'your-org-id', projectId: 'your-project-id', publicKey: 'your-base64-public-key', mode: TrustPinMode.strict, // Recommended for production );

Permissive Mode

Allows unregistered domains to bypass pinning:

await TrustPin.setup( organizationId: 'your-org-id', projectId: 'your-project-id', publicKey: 'your-base64-public-key', mode: TrustPinMode.permissive, // For development/testing );

Logging Levels

Configure SDK verbosity:

// Set before setup for complete logging await TrustPin.setLogLevel(TrustPinLogLevel.debug); // Available levels: // TrustPinLogLevel.none - No logging (production) // TrustPinLogLevel.error - Errors only // TrustPinLogLevel.info - Errors + informational messages // TrustPinLogLevel.debug - All messages (development)

Environment-Specific Setup

class TrustPinConfig { static Future<void> initializeForEnvironment() async { const environment = String.fromEnvironment( 'ENVIRONMENT', defaultValue: 'development' ); switch (environment) { case 'production': await _initializeProduction(); break; case 'staging': await _initializeStaging(); break; default: await _initializeDevelopment(); } } static Future<void> _initializeProduction() async { await TrustPin.setLogLevel(TrustPinLogLevel.error); await TrustPin.setup( organizationId: 'prod-org-123', projectId: 'prod-project-456', publicKey: 'LS0tLS1CRUdJTi...', mode: TrustPinMode.strict, ); } static Future<void> _initializeDevelopment() async { await TrustPin.setLogLevel(TrustPinLogLevel.debug); await TrustPin.setup( organizationId: 'dev-org-123', projectId: 'dev-project-456', publicKey: 'LS0tLS1CRUdJTi...', mode: TrustPinMode.permissive, ); } } // Usage in main.dart void main() async { WidgetsFlutterBinding.ensureInitialized(); await TrustPinConfig.initializeForEnvironment(); runApp(MyApp()); }

Error Handling

Exception Types

try { await TrustPin.verify('api.example.com', certificate); } on TrustPinException catch (e) { switch (e.code) { case 'PINS_MISMATCH': // Certificate doesn't match pins - CRITICAL SECURITY ISSUE print('SECURITY ALERT: Certificate mismatch'); break; case 'DOMAIN_NOT_REGISTERED': // Domain not configured (strict mode only) print('Domain not registered'); break; case 'ALL_PINS_EXPIRED': // All pins expired print('Pins expired - update needed'); break; case 'INVALID_SERVER_CERT': // Invalid certificate format print('Invalid certificate format'); break; case 'ERROR_FETCHING_PINNING_INFO': // Network error fetching configuration print('"Network error'); break; case 'INVALID_PROJECT_CONFIG': // Invalid setup parameters print('Configuration error'); break; case 'CONFIGURATION_VALIDATION_FAILED': // JWS signature validation failed print('Signature validation failed'); break; } }

Helper Properties

try { await TrustPin.verify('api.example.com', certificate); } on TrustPinException catch (e) { // Use convenience properties if (e.isPinsMismatch) { // Handle certificate mismatch } if (e.isDomainNotRegistered) { // Handle unregistered domain } if (e.isAllPinsExpired) { // Handle expired pins } }

Error Handling Pattern

class TrustPinErrorHandler { static void handleVerificationError( TrustPinException error, String domain ) { // Log for monitoring _logSecurityEvent(error, domain); switch (error.code) { case 'PINS_MISMATCH': _handlePinsMismatch(error, domain); break; case 'DOMAIN_NOT_REGISTERED': _handleDomainNotRegistered(error, domain); break; // ... handle other cases } } static void _handlePinsMismatch( TrustPinException error, String domain ) { // Critical security issue - potential MITM attack print('SECURITY ALERT: Certificate mismatch for $domain'); // Block the request and alert the user } static void _logSecurityEvent( TrustPinException error, String domain ) { final event = { 'timestamp': DateTime.now().toIso8601String(), 'domain': domain, 'error_code': error.code, 'error_message': error.message, }; // Send to your logging/monitoring service } }

Best Practices

Setup & Initialization

  1. Call WidgetsFlutterBinding.ensureInitialized() before TrustPin setup
  2. Initialize in main() before running the app
  3. Handle setup errors gracefully - don’t block app launch
  4. Set log level before setup for complete logging

Security

  1. Use TrustPinMode.strict in production
  2. Monitor pin validation failures
  3. Rotate pins before expiration
  4. Keep credentials secure (use environment variables or flutter_dotenv)
  5. Use HTTPS for all pinned domains

Performance

  1. Configuration caching is automatic (10-minute TTL)
  2. Reuse HTTP client instances
  3. Use TrustPinLogLevel.error or none in production
  4. Initialize early in app lifecycle

Development Workflow

  1. Start with TrustPinMode.permissive during development
  2. Test all endpoints with pinning enabled
  3. Validate configurations in staging
  4. Switch to TrustPinMode.strict for production releases
  5. Use TrustPinLogLevel.debug for troubleshooting

Complete Example

Main App Setup

import 'package:flutter/material.dart'; import 'package:trustpin_sdk/trustpin_sdk.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize TrustPin with environment-specific config const isProduction = bool.fromEnvironment('dart.vm.product'); await TrustPin.setLogLevel( isProduction ? TrustPinLogLevel.error : TrustPinLogLevel.debug ); try { await TrustPin.setup( organizationId: const String.fromEnvironment('TRUSTPIN_ORG_ID'), projectId: const String.fromEnvironment('TRUSTPIN_PROJECT_ID'), publicKey: const String.fromEnvironment('TRUSTPIN_PUBLIC_KEY'), mode: isProduction ? TrustPinMode.strict : TrustPinMode.permissive, ); } catch (e) { // Log but don't block app print('TrustPin setup failed: $e'); } runApp(MyApp()); }

Network Service with Dio

import 'package:dio/dio.dart'; import 'package:trustpin_sdk/trustpin_sdk.dart'; class ApiService { late final Dio _dio; ApiService() { _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 30), receiveTimeout: const Duration(seconds: 30), )); // Add TrustPin interceptor _dio.interceptors.add(TrustPinDioInterceptor()); } Future<Response> getData() async { try { return await _dio.get('/data'); } on DioException catch (e) { if (e.error is TrustPinException) { final error = e.error as TrustPinException; print('Certificate pinning failed: ${error.code}'); } rethrow; } } }

Troubleshooting

Setup Fails with INVALID_PROJECT_CONFIG

  • Verify credentials in TrustPin Dashboard 
  • Check for whitespace in credentials
  • Ensure public key is properly base64-encoded
  • Call WidgetsFlutterBinding.ensureInitialized() first

Certificate Verification Fails

  • Confirm domain is registered in dashboard
  • Verify certificate format (PEM-encoded with BEGIN/END markers)
  • Check pin expiration dates
  • Test with TrustPinMode.permissive first

iOS Build Issues

  • Ensure minimum deployment target is iOS 13.0+
  • Run pod install in the ios/ directory
  • Clean build folder (Product → Clean Build Folder in Xcode)
  • Check CocoaPods installation

Android Build Issues

  • Ensure minimum SDK is API 25+
  • Verify Kotlin version is 1.9.0+
  • Run flutter clean && flutter pub get
  • Check Gradle sync in Android Studio

macOS Sandbox Issues

  • Add network client entitlement to entitlements files
  • Ensure entitlements are properly configured in Xcode
  • Check App Sandbox settings

Network Requests Fail

  • Verify TrustPin initialized before requests
  • Enable debug logging to see detailed errors
  • Test with a simple GET request first
  • Check network connectivity

API Reference

TrustPin Class

Methods

// Setup static Future<void> setup({ required String organizationId, required String projectId, required String publicKey, Uri? configurationURL, TrustPinMode mode = TrustPinMode.strict, }) // Manual verification static Future<void> verify(String domain, String certificate) // Logging static Future<void> setLogLevel(TrustPinLogLevel level)

TrustPinHttpClient

// Constructor TrustPinHttpClient(http.Client inner) // Factory factory TrustPinHttpClient.create() // Methods Future<Response> send(BaseRequest request) void clearCertificateCache() void close()

TrustPinDioInterceptor

// Constructor TrustPinDioInterceptor() // Methods void onRequest(RequestOptions, RequestInterceptorHandler) void clearCertificateCache()

Enums

TrustPinMode

  • TrustPinMode.strict - Throws errors for unregistered domains (production)
  • TrustPinMode.permissive - Allows unregistered domains (development)

TrustPinLogLevel

  • TrustPinLogLevel.none - No logging
  • TrustPinLogLevel.error - Errors only
  • TrustPinLogLevel.info - Errors + info
  • TrustPinLogLevel.debug - All messages

TrustPinException

class TrustPinException implements Exception { final String code; final String message; final dynamic details; // Helper properties bool get isPinsMismatch; bool get isDomainNotRegistered; bool get isAllPinsExpired; // ... }

Error Codes:

  • INVALID_PROJECT_CONFIG
  • ERROR_FETCHING_PINNING_INFO
  • INVALID_SERVER_CERT
  • PINS_MISMATCH
  • ALL_PINS_EXPIRED
  • JWS_VALIDATION_FAILED
  • DOMAIN_NOT_REGISTERED
  • CONFIGURATION_VALIDATION_FAILED

Production Deployment

Production Configuration

1. Set Production Mode

import 'package:flutter/material.dart'; import 'package:trustpin_sdk/trustpin_sdk.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Load environment variables await dotenv.load(fileName: ".env.production"); // Production configuration const isProduction = bool.fromEnvironment('dart.vm.product'); await TrustPin.setLogLevel( isProduction ? TrustPinLogLevel.error : TrustPinLogLevel.debug ); await TrustPin.setup( organizationId: dotenv.env['TRUSTPIN_ORG_ID']!, projectId: dotenv.env['TRUSTPIN_PROJECT_ID']!, publicKey: dotenv.env['TRUSTPIN_PUBLIC_KEY']!, mode: TrustPinMode.strict, // Always strict in production ); runApp(MyApp()); }

2. Environment Files

Create .env.production:

TRUSTPIN_ORG_ID=your-org-id TRUSTPIN_PROJECT_ID=your-project-id TRUSTPIN_PUBLIC_KEY=your-public-key

Add to pubspec.yaml:

dependencies: flutter_dotenv: ^5.0.2 flutter: assets: - .env.production

3. Build for Release

iOS:

flutter build ios --release --dart-define=ENVIRONMENT=production

Android:

flutter build appbundle --release --dart-define=ENVIRONMENT=production

Resources