no_screenshot
A Flutter plugin to disable screenshots, block screen recording, detect screenshot events, and show a custom image overlay in the app switcher on Android, iOS, and macOS.
Features
| Feature | Android | iOS | macOS |
|---|---|---|---|
| Disable screenshot & screen recording | ✅ | ✅ | ✅ |
| Enable screenshot & screen recording | ✅ | ✅ | ✅ |
| Toggle screenshot protection | ✅ | ✅ | ✅ |
| Listen for screenshot events (stream) | ✅ | ✅ | ✅ |
| Image overlay in app switcher / recents | ✅ | ✅ | - |
Note: State is automatically persisted via native SharedPreferences / UserDefaults. You do not need to track
didChangeAppLifecycleState.
Installation
Add no_screenshot to your pubspec.yaml:
dependencies:
no_screenshot: ^0.3.3-beta.1
Then run:
flutter pub get
Quick Start
import 'package:no_screenshot/no_screenshot.dart';
final noScreenshot = NoScreenshot.instance;
// Disable screenshots & screen recording
await noScreenshot.screenshotOff();
// Re-enable screenshots & screen recording
await noScreenshot.screenshotOn();
// Toggle between enabled / disabled
await noScreenshot.toggleScreenshot();
Usage
1. Screenshot & Screen Recording Protection
Block or allow screenshots and screen recording with a single method call.
final _noScreenshot = NoScreenshot.instance;
// Disable screenshots (returns true on success)
Future<void> disableScreenshot() async {
final result = await _noScreenshot.screenshotOff();
debugPrint('screenshotOff: $result');
}
// Enable screenshots (returns true on success)
Future<void> enableScreenshot() async {
final result = await _noScreenshot.screenshotOn();
debugPrint('screenshotOn: $result');
}
// Toggle the current state
Future<void> toggleScreenshot() async {
final result = await _noScreenshot.toggleScreenshot();
debugPrint('toggleScreenshot: $result');
}
2. Screenshot Monitoring (Stream)
Listen for screenshot events in real time. Monitoring is off by default -- you must explicitly start it.
final _noScreenshot = NoScreenshot.instance;
// 1. Subscribe to the stream
_noScreenshot.screenshotStream.listen((snapshot) {
debugPrint('Protection active: ${snapshot.isScreenshotProtectionOn}');
debugPrint('Screenshot taken: ${snapshot.wasScreenshotTaken}');
debugPrint('Path: ${snapshot.screenshotPath}');
});
// 2. Start monitoring
await _noScreenshot.startScreenshotListening();
// 3. Stop monitoring when no longer needed
await _noScreenshot.stopScreenshotListening();
The stream emits a ScreenshotSnapshot object:
| Property | Type | Description |
|---|---|---|
isScreenshotProtectionOn |
bool |
Whether screenshot protection is currently active |
wasScreenshotTaken |
bool |
Whether a screenshot was just captured |
screenshotPath |
String |
Path of the captured screenshot (when available) |
3. Image Overlay (App Switcher / Recents)
Show a custom image when the app appears in the app switcher or recents screen. This prevents sensitive content from being visible in thumbnails.
final _noScreenshot = NoScreenshot.instance;
// Toggle the image overlay on/off (returns the new state)
Future<void> toggleOverlay() async {
final isActive = await _noScreenshot.toggleScreenshotWithImage();
debugPrint('Image overlay active: $isActive');
}
Setup: Place your overlay image in the platform-specific asset locations:
- Android:
android/app/src/main/res/drawable/image.png - iOS: Add an image named
imageto your asset catalog (Runner/Assets.xcassets/image.imageset/)
When enabled, the overlay image is shown whenever the app goes to the background or appears in the app switcher. Screenshot protection is also automatically activated.
Full Example
Below is a complete example showing all features together. See the full source in example/lib/main.dart.
import 'package:flutter/material.dart';
import 'package:no_screenshot/no_screenshot.dart';
import 'package:no_screenshot/screenshot_snapshot.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'No Screenshot Example',
theme: ThemeData(
colorSchemeSeed: Colors.deepPurple,
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _noScreenshot = NoScreenshot.instance;
bool _isMonitoring = false;
bool _isOverlayImageOn = false;
ScreenshotSnapshot _latestSnapshot = ScreenshotSnapshot(
isScreenshotProtectionOn: false,
wasScreenshotTaken: false,
screenshotPath: '',
);
@override
void initState() {
super.initState();
_noScreenshot.screenshotStream.listen((value) {
setState(() => _latestSnapshot = value);
if (value.wasScreenshotTaken) {
debugPrint('Screenshot taken at path: ${value.screenshotPath}');
}
});
}
// ── Screenshot Protection ──────────────────────────────────────────
Future<void> _disableScreenshot() async {
final result = await _noScreenshot.screenshotOff();
debugPrint('screenshotOff: $result');
}
Future<void> _enableScreenshot() async {
final result = await _noScreenshot.screenshotOn();
debugPrint('screenshotOn: $result');
}
Future<void> _toggleScreenshot() async {
final result = await _noScreenshot.toggleScreenshot();
debugPrint('toggleScreenshot: $result');
}
// ── Screenshot Monitoring ──────────────────────────────────────────
Future<void> _startMonitoring() async {
await _noScreenshot.startScreenshotListening();
setState(() => _isMonitoring = true);
}
Future<void> _stopMonitoring() async {
await _noScreenshot.stopScreenshotListening();
setState(() => _isMonitoring = false);
}
// ── Image Overlay ─────────────────────────────────────────────────
Future<void> _toggleScreenshotWithImage() async {
final result = await _noScreenshot.toggleScreenshotWithImage();
debugPrint('toggleScreenshotWithImage: $result');
setState(() => _isOverlayImageOn = result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('No Screenshot Example')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// Screenshot protection buttons
ElevatedButton(
onPressed: _disableScreenshot,
child: const Text('Disable Screenshot'),
),
ElevatedButton(
onPressed: _enableScreenshot,
child: const Text('Enable Screenshot'),
),
ElevatedButton(
onPressed: _toggleScreenshot,
child: const Text('Toggle Screenshot'),
),
const Divider(),
// Monitoring buttons
ElevatedButton(
onPressed: _startMonitoring,
child: const Text('Start Monitoring'),
),
ElevatedButton(
onPressed: _stopMonitoring,
child: const Text('Stop Monitoring'),
),
Text('Monitoring: $_isMonitoring'),
Text('Last snapshot: $_latestSnapshot'),
const Divider(),
// Image overlay toggle
ElevatedButton(
onPressed: _toggleScreenshotWithImage,
child: const Text('Toggle Image Overlay'),
),
Text('Overlay active: $_isOverlayImageOn'),
],
),
);
}
}
API Reference
| Method | Return Type | Description |
|---|---|---|
NoScreenshot.instance |
NoScreenshot |
Singleton instance of the plugin |
screenshotOff() |
Future<bool> |
Disable screenshots & screen recording |
screenshotOn() |
Future<bool> |
Enable screenshots & screen recording |
toggleScreenshot() |
Future<bool> |
Toggle screenshot protection on/off |
toggleScreenshotWithImage() |
Future<bool> |
Toggle image overlay mode (returns new state) |
startScreenshotListening() |
Future<void> |
Start monitoring for screenshot events |
stopScreenshotListening() |
Future<void> |
Stop monitoring for screenshot events |
screenshotStream |
Stream<ScreenshotSnapshot> |
Stream of screenshot activity events |
Contributors
Thanks to everyone who has contributed to this project!
License
BSD 3-Clause License. See LICENSE for details.







