flutter_screenshot_blocker 1.0.4
flutter_screenshot_blocker: ^1.0.4 copied to clipboard
A powerful Flutter plugin that prevents screenshots or shows black screen when screenshots are taken. This plugin provides **system-level protection** using native platform code.

Flutter Screenshot Blocker #
A powerful Flutter plugin that prevents screenshots or shows black screen when screenshots are taken. This plugin provides system-level protection using native platform code.
🌟 Features #
- ✅ System-Level Screenshot Blocking - Uses FLAG_SECURE on Android and secure views on iOS
- 📱 Black Screen on Screenshot - When screenshots are attempted, they show black screen
- 📱 Black Screen on Screen Recording - Screen recordings will show black screen
- 🔍 Screenshot Detection - Detect when users attempt to take screenshots
- 🎯 Widget-Level Protection - Apply protection to specific parts of your app
- 🔒 Secure App Wrapper - Easy-to-use app-wide protection
- 📊 Event Streaming - Real-time screenshot attempt notifications
- 🚀 Cross-Platform - Works on both Android and iOS
- 💪 Native Implementation - Maximum security with platform-specific code
📋 Requirements #
| Platform | Minimum Version |
|---|---|
| Flutter | 3.0.0+ |
| Dart | 2.17.0+ |
| iOS | 12.0+ |
| Android | API 21 (Android 5.0)+ |
🚀 Installation #
Add this to your pubspec.yaml:
dependencies:
flutter_screenshot_blocker: ^1.0.4
Run:
flutter pub get
📱 Platform Setup #
🤖 Android Setup #
No additional setup required! The plugin automatically applies FLAG_SECURE to prevent screenshots. You can setup it if needed. So you can use the manifest given below.
To block screenshots in your Flutter app, update the AndroidManifest.xml as shown below: (Optional)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_screenshot_blocker_example">
<!-- (Optional) Permission for detecting screenshots -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:label="flutter_screenshot_blocker_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Enable FLAG_SECURE to block screenshots and screen recordings -->
<meta-data
android:name="flutter_screenshot_blocker.secure"
android:value="true" />
<!-- Flutter theme metadata -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
🍎 iOS Setup #
No additional setup required! The plugin uses the secure UITextField technique and screenshot detection. You can setup it if needed. So you can use the Info.plist given below.
To enable screenshot protection and enhance app security on iOS, add the following keys to your ios/Runner/Info.plist file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- ... existing keys ... -->
<!-- Add screenshot protection info -->
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs to detect screenshots for security purposes.</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<!-- Prevent screenshots in app switcher -->
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
</dict>
</plist>
📖 Usage #
Add the import at the top of your Dart file:
import 'package:flutter_screenshot_blocker/flutter_screenshot_blocker.dart';
Method 1: Secure App Wrapper (Easiest) #
Wrap your entire app with SecureApp in main() — one-line setup that protects every screen automatically. Optionally shows a security warning dialog on first launch.
void main() {
runApp(
SecureApp(
enableScreenshotBlocking: true, // block screenshots across the whole app
showSecurityWarning: true, // show a security dialog on first launch
securityWarningMessage: 'This app is protected. Screenshots are disabled.', // optional custom message
child: MyApp(),
),
);
}
| Property | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | Your root app widget |
enableScreenshotBlocking |
bool |
true |
Block screenshots app-wide |
showSecurityWarning |
bool |
true |
Show security dialog on launch |
securityWarningMessage |
String? |
null |
Custom message in the dialog |
Method 2: Widget-Level Protection #
Wrap only the sensitive part of your UI — not the whole app. blockScreenshots blocks capture, detectScreenshots fires a callback when a screenshot is attempted, and child is the widget you want to protect.
ScreenshotBlockerWidget(
blockScreenshots: true, // makes this widget appear black in screenshots
detectScreenshots: true, // listen for screenshot attempts
onScreenshotDetected: (event) {
// event.type → 'screenshot_taken' or 'screenshot_blocked'
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Screenshots are not allowed!')),
);
},
child: YourSensitiveWidget(), // e.g. bank balance, OTP, personal info
);
| Property | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | The widget to protect |
blockScreenshots |
bool |
true |
Block capture — shows black in screenshots |
detectScreenshots |
bool |
true |
Enable screenshot attempt detection |
onScreenshotDetected |
Function(ScreenshotEvent)? |
null |
Callback when screenshot is attempted |
blockedWidget |
Widget? |
null |
Custom widget shown while protection is active |
Method 3: Manual Control #
Enable blocking in initState() when the page opens and disable it in dispose() when the page closes.
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
StreamSubscription? _screenshotSubscription;
@override
void initState() {
super.initState();
// Enable protection when page loads
FlutterScreenshotBlocker.enableScreenshotBlocking();
}
@override
void dispose() {
// Disable protection when leaving page
FlutterScreenshotBlocker.disableScreenshotBlocking();
_screenshotSubscription?.cancel();
super.dispose();
}
}
Screenshot Detection Events #
Call enableScreenshotDetection() first, then listen to onScreenshotDetected stream. Two events are fired — screenshot_taken (user took a screenshot) shows a dialog, and screenshot_blocked (screenshot was blocked) shows a snackbar. Always cancel the subscription in dispose() to avoid memory leaks, and call disableScreenshotDetection() to stop native callbacks.
@override
void initState() {
super.initState();
// Step 1: enable native screenshot detection
FlutterScreenshotBlocker.enableScreenshotDetection();
// Step 2: listen to events
_screenshotSubscription = FlutterScreenshotBlocker.onScreenshotDetected
.listen((ScreenshotEvent event) {
switch (event.type) {
case 'screenshot_taken':
// Fires when user takes a screenshot — show a warning dialog
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Screenshot Detected!'),
content: Text('Screenshots are not allowed in this app.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
break;
case 'screenshot_blocked':
// Fires when a screenshot attempt was blocked — show a snackbar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Screenshot blocked for security!'),
backgroundColor: Colors.red,
),
);
break;
}
});
}
@override
void dispose() {
// Step 3: always clean up to prevent memory leaks
FlutterScreenshotBlocker.disableScreenshotDetection();
_screenshotSubscription?.cancel();
super.dispose();
}
| Event type | When it fires | Recommended action |
|---|---|---|
screenshot_taken |
User took a screenshot | Show warning dialog |
screenshot_blocked |
Screenshot was blocked by the plugin | Show snackbar / log |
Check & Toggle Protection State #
// Check if blocking is currently active
bool isEnabled = await FlutterScreenshotBlocker.isScreenshotBlockingEnabled();
// Toggle using a single method
await FlutterScreenshotBlocker.setSecureFlag(true); // enable
await FlutterScreenshotBlocker.setSecureFlag(false); // disable
ScreenshotEvent Model #
Every event from onScreenshotDetected is a ScreenshotEvent object with these fields:
| Field | Type | Description |
|---|---|---|
type |
String |
'screenshot_taken' or 'screenshot_blocked' |
timestamp |
DateTime |
When the event occurred |
metadata |
Map<String, dynamic>? |
Extra info — e.g. platform: 'android' |
FlutterScreenshotBlocker.onScreenshotDetected.listen((ScreenshotEvent event) {
print(event.type); // 'screenshot_taken'
print(event.timestamp); // 2024-01-01 12:00:00.000
print(event.metadata?['platform']); // 'ios' or 'android'
});
⚙️ API Reference #
FlutterScreenshotBlocker Class #
| Method | Return Type | Description |
|---|---|---|
enableScreenshotBlocking() |
Future<bool> |
Enable screenshot blocking |
disableScreenshotBlocking() |
Future<bool> |
Disable screenshot blocking |
isScreenshotBlockingEnabled() |
Future<bool> |
Check if blocking is currently enabled |
enableSecureMode() |
Future<bool> |
Alias for enableScreenshotBlocking() |
disableSecureMode() |
Future<bool> |
Alias for disableScreenshotBlocking() |
setSecureFlag(bool) |
Future<bool> |
Enable or disable blocking with a single call |
enableScreenshotDetection() |
Future<bool> |
Start listening for screenshot attempts |
disableScreenshotDetection() |
Future<bool> |
Stop listening for screenshot attempts |
onScreenshotDetected |
Stream<ScreenshotEvent> |
Stream of screenshot events |
ScreenshotBlockerWidget Properties #
| Property | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | Widget to protect |
blockScreenshots |
bool |
true |
Enable screenshot blocking |
detectScreenshots |
bool |
true |
Enable screenshot detection |
onScreenshotDetected |
Function(ScreenshotEvent)? |
null |
Screenshot event callback |
blockedWidget |
Widget? |
null |
Custom widget when blocked |
SecureApp Properties #
| Property | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | App widget |
enableScreenshotBlocking |
bool |
true |
Enable protection |
showSecurityWarning |
bool |
true |
Show security dialog |
securityWarningMessage |
String? |
null |
Custom warning message |
🔒 How It Works #
Android Implementation #
- Uses
WindowManager.LayoutParams.FLAG_SECUREat the window level - The OS marks the window as non-capturable — screenshots and recordings show black
- Screenshot detection uses
ContentObserverwatching the media store for new images
iOS Implementation #
- Creates a full-screen
UITextFieldwithisSecureTextEntry = true - Reparents Flutter's root view layer (
flutterView.layer) into the UITextField's protected IOSurface subtree - The OS compositor marks that IOSurface as non-capturable — screenshots show black
- Screen recording detection uses
UIScreen.capturedDidChangeNotification; when recording is active, a solid blackUIWindowoverlays the app at a high window level - Screenshot detection uses
UIApplication.userDidTakeScreenshotNotification
⚠️ Important Notes #
Security Level #
This plugin provides system-level protection which means:
- ✅ Screenshots will show black screen
- ✅ Screen recordings will show black screen
- ✅ App switcher will show protected content
- ❌ Cannot prevent physical camera recording of screen
- ❌ Cannot prevent rooted/jailbroken device bypasses
Platform Differences #
- Android: Uses
FLAG_SECURE— very effective across all Android versions - iOS: Uses secure
UITextFieldlayer technique — requires a real device (not Simulator) - Web: Not supported (web platform limitations) - Next Feature(Coming Soon)
⚠️ iOS Simulator Limitation - Use always real device #
Screenshot blocking does NOT work on iOS Simulator. Always test on a real iOS device.
| Feature | iOS Simulator | Real iOS Device |
|---|---|---|
| Screenshot blocking (black screen) | ❌ | ✅ |
| Screen recording overlay (black) | ❌ | ✅ |
| Screenshot detection event | ⚠️ Unreliable | ✅ |
| UI / layout testing | ✅ | ✅ |
Why Simulator doesn't work:
- iOS Simulator runs on macOS and uses macOS's display compositor, not the iOS compositor
- The
IOSurfacehardware-level protection flag is an iOS-only hardware feature — macOS does not emulate it - The Simulator is just a macOS window — macOS can freely screenshot everything inside it
UIScreen.capturedDidChangeNotificationfor recording detection also doesn't fire correctly
⚠️ Android Emulator Limitation - Use always real device #
Screenshot blocking partially works on Android Emulator — unlike iOS Simulator, it is not a complete failure, but full testing requires a real device.
| Feature | Android Emulator | Real Android Device |
|---|---|---|
| Screenshot blocking (system/ADB) | ✅ | ✅ |
| Screenshot blocking (host OS capture) | ❌ | ✅ |
| Screen recording blocking | ⚠️ Partial | ✅ |
| Screenshot detection event | ✅ | ✅ |
| UI / layout testing | ✅ | ✅ |
How Emulator differs from a real device:
FLAG_SECUREblocks Android's own screenshot mechanism —adb screencapand Android Studio's screenshot button will show black ✅- But the emulator is a window on your host machine (macOS/Windows/Linux) — taking a screenshot of the host OS (e.g.
Cmd+Shift+4on Mac) captures the emulator window directly, bypassingFLAG_SECURE❌ - For full end-to-end testing of screenshot and recording blocking, always use a real Android device
To test on a real device:
# List connected devices
flutter devices
# Run on a specific device
flutter run -d <device-udid>
🔧 Troubleshooting #
Screenshots Still Visible #
- Make sure
enableScreenshotBlocking()was called and returnedtrue - On iOS, always test on a real device — Simulator will never show black
- On iOS, ensure the app is in the foreground when blocking is enabled
- Try
SecureAppwrapper for guaranteed app-wide protection
Detection Not Working #
- Ensure
enableScreenshotDetection()was called before listening to the stream - Check you are subscribed to
onScreenshotDetectedstream - On Android, some OEM devices may delay or suppress screenshot notifications
- On iOS Simulator, detection events are unreliable — use a real device
Screen Recording Not Showing Black (iOS) #
- Screen recording overlay relies on
UIScreen.capturedDidChangeNotification - This only fires on real devices — not on Simulator
- Ensure
enableScreenshotBlocking()was called (the capture observer is set up inside it)
Performance Issues #
- Screenshot blocking has minimal performance impact
- Detection uses system notifications — very efficient
- Consider disabling detection when not needed to reduce overhead
🤝 Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License #
This project is licensed under the MIT License.
🎯 Use Cases #
Perfect for:
- 🏦 Banking Apps - Protect account numbers and balances
- 🏥 Medical Apps - Secure patient information
- 💼 Business Apps - Protect confidential documents
- 🔐 Security Apps - Prevent sensitive data leakage
- 📱 Enterprise Apps - Meet corporate security requirements
⭐ Star This Repo #
If this plugin helped you, please ⭐ star this repository!