Flutter SDK
Integrate TrustPin into your Flutter application for cross-platform certificate pinning on iOS, Android, and macOS.
Platform Support
| Platform | Minimum Version | Native SDK |
|---|---|---|
| iOS | 13.0+ | TrustPin Swift SDK |
| Android | API 21 (Android 5.0 Lollipop) | TrustPin Kotlin SDK |
| macOS | 13.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
pub.dev (Recommended)
Add TrustPin to your pubspec.yaml:
dependencies:
trustpin_sdk: ^2.0.0Then install:
flutter pub getGit (Development)
For the latest development version:
dependencies:
trustpin_sdk:
git:
url: https://github.com/trustpin-cloud/trustpin-libraries.git
path: flutter/trustpin_sdkPlatform 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
- Call
WidgetsFlutterBinding.ensureInitialized()before TrustPin setup - Initialize in
main()before running the app - Handle setup errors gracefully - don’t block app launch
- Set log level before setup for complete logging
Security
- Use
TrustPinMode.strictin production - Monitor pin validation failures
- Rotate pins before expiration
- Keep credentials secure (use environment variables or flutter_dotenv)
- Use HTTPS for all pinned domains
Performance
- Configuration caching is automatic (10-minute TTL)
- Reuse HTTP client instances
- Use
TrustPinLogLevel.errorornonein production - Initialize early in app lifecycle
Development Workflow
- Start with
TrustPinMode.permissiveduring development - Test all endpoints with pinning enabled
- Validate configurations in staging
- Switch to
TrustPinMode.strictfor production releases - Use
TrustPinLogLevel.debugfor 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.permissivefirst
iOS Build Issues
- Ensure minimum deployment target is iOS 13.0+
- Run
pod installin theios/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 loggingTrustPinLogLevel.error- Errors onlyTrustPinLogLevel.info- Errors + infoTrustPinLogLevel.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_CONFIGERROR_FETCHING_PINNING_INFOINVALID_SERVER_CERTPINS_MISMATCHALL_PINS_EXPIREDJWS_VALIDATION_FAILEDDOMAIN_NOT_REGISTEREDCONFIGURATION_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-keyAdd to pubspec.yaml:
dependencies:
flutter_dotenv: ^5.0.2
flutter:
assets:
- .env.production3. Build for Release
iOS:
flutter build ios --release --dart-define=ENVIRONMENT=productionAndroid:
flutter build appbundle --release --dart-define=ENVIRONMENT=productionResources
- Package: pub.dev/packages/trustpin_sdk
- Repository: github.com/trustpin-cloud/trustpin-libraries
- API Docs: pub.dev/documentation/trustpin_sdk
- Dashboard: app.trustpin.cloud
- Support: support@trustpin.cloud