no_screenshot 0.4.0 copy "no_screenshot: ^0.4.0" to clipboard
no_screenshot: ^0.4.0 copied to clipboard

Flutter plugin to enable, disable, toggle or stream screenshot and screen recording activities in your application.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:no_screenshot/no_screenshot.dart';
import 'package:no_screenshot/screenshot_snapshot.dart';

import 'app_localizations.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isRTL = false;

  void _toggleRTL(bool value) {
    setState(() => _isRTL = value);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'No Screenshot Example',
      theme: ThemeData(
        colorSchemeSeed: Colors.deepPurple,
        useMaterial3: true,
      ),
      locale: _isRTL ? const Locale('ar') : const Locale('en'),
      supportedLocales: AppLocalizations.supportedLocales,
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      home: HomePage(isRTL: _isRTL, onRTLChanged: _toggleRTL),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key, required this.isRTL, required this.onRTLChanged});

  final bool isRTL;
  final ValueChanged<bool> onRTLChanged;

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _noScreenshot = NoScreenshot.instance;
  bool _isMonitoring = false;
  bool _isRecordingMonitoring = 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}');
        _showScreenshotAlert(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);
  }

  // ── Recording Monitoring ───────────────────────────────────────────

  Future<void> _startRecordingMonitoring() async {
    await _noScreenshot.startScreenRecordingListening();
    setState(() => _isRecordingMonitoring = true);
  }

  Future<void> _stopRecordingMonitoring() async {
    await _noScreenshot.stopScreenRecordingListening();
    setState(() => _isRecordingMonitoring = false);
  }

  // ── Set Overlay Image ──────────────────────────────────────────────

  Future<void> _toggleScreenshotWithImage() async {
    final result = await _noScreenshot.toggleScreenshotWithImage();
    debugPrint('toggleScreenshotWithImage: $result');
    setState(() => _isOverlayImageOn = result);
  }

  // ── UI ─────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    final l = AppLocalizations.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(l.appTitle),
        actions: [
          Text(l.rtl),
          Switch(
            value: widget.isRTL,
            onChanged: widget.onRTLChanged,
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildSection(
            title: l.protectionSectionTitle,
            subtitle: l.platformSubtitle,
            children: [
              _StatusRow(
                label: l.protection,
                isOn: _latestSnapshot.isScreenshotProtectionOn,
              ),
              const SizedBox(height: 12),
              Row(
                children: [
                  Expanded(
                    child: _FeatureButton(
                      label: l.disableScreenshot,
                      subtitle: l.blocksCapture,
                      onPressed: _disableScreenshot,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: _FeatureButton(
                      label: l.enableScreenshot,
                      subtitle: l.allowsCapture,
                      onPressed: _enableScreenshot,
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 8),
              _FeatureButton(
                label: l.toggleScreenshot,
                subtitle: l.toggleScreenshotSubtitle,
                onPressed: _toggleScreenshot,
              ),
            ],
          ),
          const SizedBox(height: 16),
          _buildSection(
            title: l.monitoringSectionTitle,
            subtitle: l.platformSubtitle,
            children: [
              _StatusRow(
                label: l.monitoring,
                isOn: _isMonitoring,
              ),
              const SizedBox(height: 8),
              _SnapshotInfo(snapshot: _latestSnapshot),
              const SizedBox(height: 12),
              Row(
                children: [
                  Expanded(
                    child: _FeatureButton(
                      label: l.enableMonitoring,
                      subtitle: l.startListening,
                      onPressed: _startMonitoring,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: _FeatureButton(
                      label: l.disableMonitoring,
                      subtitle: l.stopListening,
                      onPressed: _stopMonitoring,
                    ),
                  ),
                ],
              ),
            ],
          ),
          const SizedBox(height: 16),
          _buildSection(
            title: l.recordingMonitoringSectionTitle,
            subtitle: l.platformSubtitle,
            children: [
              _StatusRow(
                label: l.recordingMonitoring,
                isOn: _isRecordingMonitoring,
              ),
              const SizedBox(height: 8),
              _StatusRow(
                label: l.screenRecording,
                isOn: _latestSnapshot.isScreenRecording,
              ),
              const SizedBox(height: 12),
              Row(
                children: [
                  Expanded(
                    child: _FeatureButton(
                      label: l.enableRecordingMonitoring,
                      subtitle: l.startRecordingListening,
                      onPressed: _startRecordingMonitoring,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: _FeatureButton(
                      label: l.disableRecordingMonitoring,
                      subtitle: l.stopRecordingListening,
                      onPressed: _stopRecordingMonitoring,
                    ),
                  ),
                ],
              ),
            ],
          ),
          const SizedBox(height: 16),
          _buildSection(
            title: l.overlaySectionTitle,
            subtitle: l.platformSubtitle,
            children: [
              _StatusRow(
                label: l.overlay,
                isOn: _isOverlayImageOn,
              ),
              const SizedBox(height: 12),
              _FeatureButton(
                label: l.toggleScreenshotWithImage,
                subtitle: l.overlaySubtitle,
                onPressed: _toggleScreenshotWithImage,
              ),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildSection({
    required String title,
    required String subtitle,
    required List<Widget> children,
  }) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Wrap(
              runAlignment: WrapAlignment.center,
              children: [
                Text(
                  title,
                  style: Theme.of(context).textTheme.titleMedium,
                ),
                const SizedBox(width: 8),
                Chip(
                  label: Text(subtitle),
                  visualDensity: VisualDensity.compact,
                ),
              ],
            ),
            const SizedBox(height: 12),
            ...children,
          ],
        ),
      ),
    );
  }

  void _showScreenshotAlert(String path) {
    final l = AppLocalizations.of(context);
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        icon: const Icon(Icons.warning_amber_outlined,
            size: 48, color: Colors.red),
        title: Text(l.screenshotDetected),
        content: Text('${l.path}: $path'),
        actions: [
          FilledButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text(l.ok),
          ),
        ],
      ),
    );
  }
}

// ── Reusable Widgets ───────────────────────────────────────────────────

class _FeatureButton extends StatelessWidget {
  const _FeatureButton({
    required this.label,
    required this.subtitle,
    required this.onPressed,
  });

  final String label;
  final String subtitle;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity,
      child: OutlinedButton(
        onPressed: onPressed,
        style: OutlinedButton.styleFrom(
          padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
          alignment: AlignmentDirectional.centerStart,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(6),
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(label),
            Text(
              subtitle,
              style: Theme.of(context).textTheme.bodySmall,
            ),
          ],
        ),
      ),
    );
  }
}

class _StatusRow extends StatelessWidget {
  const _StatusRow({required this.label, required this.isOn});

  final String label;
  final bool isOn;

  @override
  Widget build(BuildContext context) {
    final l = AppLocalizations.of(context);
    return Row(
      children: [
        Text('$label: ', style: Theme.of(context).textTheme.bodyMedium),
        Container(
          width: 12,
          height: 12,
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            color: isOn ? Colors.green : Colors.grey,
          ),
        ),
        const SizedBox(width: 6),
        Text(isOn ? l.on : l.off),
      ],
    );
  }
}

class _SnapshotInfo extends StatelessWidget {
  const _SnapshotInfo({required this.snapshot});

  final ScreenshotSnapshot snapshot;

  @override
  Widget build(BuildContext context) {
    final l = AppLocalizations.of(context);
    final style = Theme.of(context).textTheme.bodySmall;
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceContainerHighest,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('${l.protectionActive}: ${snapshot.isScreenshotProtectionOn}',
              style: style?.copyWith(
                  color: snapshot.isScreenshotProtectionOn
                      ? Colors.green
                      : Colors.red)),
          Text('${l.screenshotTaken}: ${snapshot.wasScreenshotTaken}',
              style: style),
          Text('${l.screenRecording}: ${snapshot.isScreenRecording}',
              style: style?.copyWith(
                  color: snapshot.isScreenRecording
                      ? Colors.red
                      : null)),
          Text('${l.path}: ${snapshot.screenshotPath}', style: style),
        ],
      ),
    );
  }
}
238
likes
160
points
80.7k
downloads

Publisher

verified publisherflutterplaza.com

Weekly Downloads

Flutter plugin to enable, disable, toggle or stream screenshot and screen recording activities in your application.

Homepage
Repository (GitHub)

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on no_screenshot

Packages that implement no_screenshot