Flutter Interview Series
Master the security concepts every Flutter developer needs to know — from runtime permissions to secure data storage
Mobile app security is no longer optional — it's a fundamental expectation. In Flutter interviews, questions about permissions and security basics reveal whether a candidate understands how to build apps that protect user data and comply with platform guidelines.
This guide covers the most frequently asked interview questions on this topic, with clear, actionable answers you can use to demonstrate your expertise.
Q1: How do you handle runtime permissions in Flutter?
Sample Answer:
Flutter doesn't have a built-in permission API, so we use the permission_handler package to manage runtime permissions across iOS and Android.
import 'package:permission_handler/permission_handler.dart';
Future<void> requestCameraPermission() async {
final status = await Permission.camera.status;
if (status.isDenied) {
final result = await Permission.camera.request();
if (result.isGranted) {
// Proceed with camera access
} else if (result.isPermanentlyDenied) {
// Guide user to app settings
await openAppSettings();
}
}
}Key points to mention:
- Always check permission status before requesting
- Handle
permanentlyDeniedstate by directing users to system settings - Request permissions contextually, not at app launch
- On iOS, you must add usage descriptions in
Info.plist - On Android 13+, granular media permissions replaced
READ_EXTERNAL_STORAGE
Q2: What's the difference between iOS and Android permission models?
Sample Answer:
The two platforms handle permissions differently:
Android:
- Permissions declared in
AndroidManifest.xml - Dangerous permissions require runtime requests
- Users can revoke permissions anytime from settings
- Android 11+ introduced one-time permissions for location, camera, and microphone
- Android 14 added partial photo access
iOS:
- All sensitive permissions require runtime authorization
- Usage description strings are mandatory in
Info.plist - iOS offers "Allow Once" for location
- iOS 14+ requires ATT (App Tracking Transparency) for cross-app tracking
- Photo library offers "Limited Access" option
<!-- iOS Info.plist -->
<key>NSCameraUsageDescription</key>
<string>We need camera access to take profile photos</string>
<!-- Android AndroidManifest.xml -->
<uses-permission android:name="android.permission.CAMERA"/>Q3: How do you securely store sensitive data in Flutter?
Sample Answer:
For sensitive data like tokens, API keys, or user credentials, I use flutter_secure_storage, which leverages platform-native secure storage mechanisms.
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorageService {
final _storage = const FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
Future<void> saveToken(String token) async {
await _storage.write(key: 'auth_token', value: token);
}
Future<String?> getToken() async {
return await _storage.read(key: 'auth_token');
}
Future<void> deleteToken() async {
await _storage.delete(key: 'auth_token');
}
}Why this matters:
- On iOS, data is stored in the Keychain
- On Android, it uses EncryptedSharedPreferences (API 23+)
- Never use
SharedPreferencesfor sensitive data — it stores plain text - Consider
KeychainAccessibilityoptions for iOS background access requirements
Q4: How do you secure network communications in a Flutter app?
Sample Answer:
Securing network traffic involves multiple layers:
- Enforce HTTPS:
// Dio configuration with security options
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 10),
));2. Implement Certificate Pinning:
// Using dio with certificate pinning
import 'dart:io';
class ApiClient {
Dio createSecureDio() {
final dio = Dio();
(dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
final client = HttpClient();
client.badCertificateCallback = (cert, host, port) {
// Compare certificate fingerprint
final expectedFingerprint = 'YOUR_CERT_FINGERPRINT';
final certFingerprint = sha256.convert(cert.der).toString();
return certFingerprint == expectedFingerprint;
};
return client;
};
return dio;
}
}3. Configure platform-specific network security:
For Android, create network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
</domain-config>
</network-security-config>Q5: What is obfuscation and why is it important?
Sample Answer:
Obfuscation makes reverse engineering more difficult by renaming classes, methods, and variables to meaningless names.
Enable obfuscation for release builds:
flutter build apk --obfuscate --split-debug-info=build/debug-info
flutter build ios --obfuscate --split-debug-info=build/debug-infoWhy it matters:
- Protects business logic from competitors
- Makes it harder to find security vulnerabilities
- Reduces APK/IPA size slightly
- Required by many enterprise security policies
Important: Always save the symbol files from --split-debug-info — you'll need them to decode crash reports.
Q6: How do you protect API keys in a Flutter application?
Sample Answer:
API keys should never be hardcoded in source code. Here's my approach:
- Use compile-time environment variables:
// Define during build
// flutter build apk --dart-define=API_KEY=your_key_here
class AppConfig {
static const apiKey = String.fromEnvironment('API_KEY');
}2. For additional security, use native platform storage:
// Android: Store in local.properties (not committed to git)
// Access via BuildConfig or native method channel3. Consider backend proxy for highly sensitive keys:
- Don't embed third-party API keys in the app
- Route requests through your backend
- Your server adds the API key server-side
What NOT to do:
- Don't commit
.envfiles with real keys - Don't store keys in
assets/folder - Don't rely solely on obfuscation — determined attackers can still extract strings
Q7: How do you implement biometric authentication?
Sample Answer:
The local_auth package provides biometric authentication for both platforms:
import 'package:local_auth/local_auth.dart';
class BiometricAuthService {
final _localAuth = LocalAuthentication();
Future<bool> isBiometricAvailable() async {
final canCheck = await _localAuth.canCheckBiometrics;
final isDeviceSupported = await _localAuth.isDeviceSupported();
return canCheck && isDeviceSupported;
}
Future<List<BiometricType>> getAvailableBiometrics() async {
return await _localAuth.getAvailableBiometrics();
}
Future<bool> authenticate() async {
try {
return await _localAuth.authenticate(
localizedReason: 'Authenticate to access your account',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: false, // Allow PIN/pattern as fallback
),
);
} on PlatformException catch (e) {
// Handle errors (no biometrics enrolled, etc.)
return false;
}
}
}Configuration required:
For iOS, add to Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>We use Face ID to securely log you in</string>For Android, add to AndroidManifest.xml:
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>Q8: How do you prevent screenshot capture for sensitive screens?
Sample Answer:
This requires platform-specific implementation via method channels:
Android (MainActivity.kt):
import android.view.WindowManager
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "app/security")
.setMethodCallHandler { call, result ->
when (call.method) {
"enableSecureMode" -> {
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
result.success(true)
}
"disableSecureMode" -> {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
result.success(true)
}
else -> result.notImplemented()
}
}
}
}Flutter side:
class SecurityService {
static const _channel = MethodChannel('app/security');
static Future<void> enableSecureMode() async {
await _channel.invokeMethod('enableSecureMode');
}
static Future<void> disableSecureMode() async {
await _channel.invokeMethod('disableSecureMode');
}
}Note: iOS doesn't provide a direct equivalent. You can detect screenshots using UIApplication.userDidTakeScreenshotNotification but cannot prevent them.
Q9: What security considerations apply to deep links?
Sample Answer:
Deep links can be attack vectors if not properly validated:
- Validate all deep link parameters:
class DeepLinkHandler {
void handleDeepLink(Uri uri) {
// Never trust deep link data
final userId = uri.queryParameters['userId'];
if (userId != null && _isValidUserId(userId)) {
// Additional authorization check
if (_currentUser.canAccessUser(userId)) {
_navigateToUserProfile(userId);
}
}
}
bool _isValidUserId(String id) {
// Validate format, length, characters
return RegExp(r'^[a-zA-Z0-9]{8,32}$').hasMatch(id);
}
}2. Use App Links (Android) and Universal Links (iOS):
- These require server-side verification files
- They prevent other apps from hijacking your deep links
- More secure than custom URL schemes
3. Never include sensitive data in deep links:
- No tokens or passwords
- No personally identifiable information
- Use short-lived, one-time codes if needed
Q10: How do you detect if an app is running on a rooted/jailbroken device?
Sample Answer:
While no detection is foolproof, you can implement basic checks using packages like flutter_jailbreak_detection:
import 'package:flutter_jailbreak_detection/flutter_jailbreak_detection.dart';
class DeviceSecurityService {
Future<bool> isDeviceCompromised() async {
final isJailbroken = await FlutterJailbreakDetection.jailbroken;
final isDeveloperMode = await FlutterJailbreakDetection.developerMode;
return isJailbroken || isDeveloperMode;
}
Future<void> enforceSecurityPolicy() async {
if (await isDeviceCompromised()) {
// Options:
// 1. Show warning but allow usage
// 2. Disable sensitive features
// 3. Prevent app usage entirely (banking apps)
}
}
}Important considerations:
- Detection methods can be bypassed by sophisticated tools
- Consider your app's risk profile — banking apps need stricter policies
- Some legitimate users have rooted devices for valid reasons
- Provide clear messaging about why the restriction exists
Key Takeaways for Your Interview
- Always request permissions contextually — explain why you need access right when it's needed
- Use platform-appropriate secure storage — Keychain for iOS, EncryptedSharedPreferences for Android
- Never hardcode secrets — use environment variables or backend proxies
- Defense in depth — combine obfuscation, certificate pinning, and input validation
- Stay current — permission models evolve with each OS version
Security isn't a feature you add at the end — it's a mindset that should influence every architectural decision you make.
#Flutter #FlutterDeveloper #MobileAppSecurity #FlutterInterview #AppPermissions #FlutterSecurity #MobileDevelopment #TechInterview #iOSDevelopment #AndroidDevelopment #SecureCoding #FlutterTips