You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter如何自定义防截屏模板?实现类似WhatsApp的效果

在Flutter中实现类似WhatsApp的自定义防截屏效果

你已经通过flutter_windowmanager实现了基础防截屏功能(触发时显示黑色占位图),想要改成类似WhatsApp的自定义效果——这在Flutter中是可以实现的,但需要结合原生平台代码来完成,因为系统级的防截屏占位逻辑本身由原生层控制。

以下是你的现有实现代码:

import 'package:flutter/material.dart';
import 'package:flutter_windowmanager/flutter_windowmanager.dart';

class SecureScreen extends StatefulWidget {
  @override
  State<SecureScreen> createState() => _SecureScreenState();
}

class _SecureScreenState extends State<SecureScreen> {
  @override
  void initState() {
    super.initState();
    _enableScreenshotPrevention();
  }

  @override
  void dispose() {
    _disableScreenshotPrevention();
    super.dispose();
  }

  Future<void> _enableScreenshotPrevention() async {
    await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
  }

  Future<void> _disableScreenshotPrevention() async {
    await FlutterWindowManager.clearFlags(FlutterWindowManager.FLAG_SECURE);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Secure Screen")),
      body: const Center(
        child: Text("Screenshot prevention is active on this screen."),
      ),
    );
  }
}

WhatsApp的自定义防截屏效果示例:
WhatsApp防截屏效果


实现方案

Android和iOS的防截屏机制不同,需要分别处理:

一、Android端:替换FLAG_SECURE的默认黑色占位

Android的FLAG_SECURE会强制系统用黑色图作为截屏/录屏的占位,但我们可以通过自定义Window的DecorView,替换成自己的布局。

1. Android原生代码(Kotlin)

在MainActivity中通过MethodChannel和Flutter交互,设置自定义占位视图:

import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.WindowManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example/secure_screen"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "setCustomSecureOverlay" -> {
                    setCustomSecureOverlay()
                    result.success(null)
                }
                "clearCustomSecureOverlay" -> {
                    clearCustomSecureOverlay()
                    result.success(null)
                }
                else -> result.notImplemented()
            }
        }
    }

    private fun setCustomSecureOverlay() {
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        // 加载自定义防截屏布局
        val overlayView = LayoutInflater.from(this).inflate(R.layout.custom_secure_overlay, null)
        val decorView = window.decorView as ViewGroup
        decorView.addView(overlayView)
        // 设置布局覆盖全屏
        val params = overlayView.layoutParams as ViewGroup.LayoutParams
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        params.height = ViewGroup.LayoutParams.MATCH_PARENT
        overlayView.layoutParams = params
    }

    private fun clearCustomSecureOverlay() {
        val decorView = window.decorView as ViewGroup
        val overlayView = decorView.findViewById<android.view.View>(R.id.custom_secure_overlay)
        overlayView?.let { decorView.removeView(it) }
        window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
    }
}

2. 创建自定义布局文件

在res/layout/custom_secure_overlay.xml中定义WhatsApp风格的占位UI:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_secure_overlay"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000">

    <ImageView
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_centerInParent="true"
        android:src="@drawable/whatsapp_logo" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/imageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:text="无法截图"
        android:textColor="#FFFFFF"
        android:textSize="18sp" />

</RelativeLayout>

二、iOS端:监听截屏状态显示覆盖层

iOS没有类似FLAG_SECURE的系统级强制占位,需要通过监听UIScreen.isCaptured状态,在Flutter层显示自定义覆盖视图。

1. iOS原生代码(Swift)

在MainViewController中监听截屏状态,并通过MethodChannel通知Flutter:

import UIKit
import Flutter

class MainViewController: FlutterViewController {
    private let CHANNEL = "com.example/secure_screen"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let methodChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: self.binaryMessenger)
        methodChannel.setMethodCallHandler { [weak self] call, result in
            guard let self = self else { return }
            switch call.method {
            case "isCaptured":
                result.success(UIScreen.main.isCaptured)
            default:
                result.notImplemented()
            }
        }
        
        // 监听截屏状态变化通知
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.screenCaptureStatusChanged),
            name: UIScreen.capturedDidChangeNotification,
            object: nil
        )
    }
    
    @objc private func screenCaptureStatusChanged() {
        let methodChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: self.binaryMessenger)
        methodChannel.invokeMethod("onCaptureChanged", arguments: UIScreen.main.isCaptured)
    }
}

2. Flutter端代码修改

通过MethodChannel获取状态,在页面上叠加自定义覆盖层:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class SecureScreen extends StatefulWidget {
  @override
  State<SecureScreen> createState() => _SecureScreenState();
}

class _SecureScreenState extends State<SecureScreen> with WidgetsBindingObserver {
  static const MethodChannel _channel = MethodChannel('com.example/secure_screen');
  bool _isCaptured = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _checkCaptureStatus();
    // 监听原生发送的状态变化
    _channel.setMethodCallHandler((call) async {
      if (call.method == "onCaptureChanged") {
        setState(() {
          _isCaptured = call.arguments as bool;
        });
      }
    });
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  Future<void> _checkCaptureStatus() async {
    try {
      final status = await _channel.invokeMethod<bool>('isCaptured');
      setState(() {
        _isCaptured = status ?? false;
      });
    } on PlatformException catch (e) {
      print("获取截屏状态失败: ${e.message}");
    }
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _checkCaptureStatus();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Scaffold(
          appBar: AppBar(title: const Text("安全屏幕")),
          body: const Center(
            child: Text("此屏幕已启用自定义防截屏功能。"),
          ),
        ),
        // 截屏时显示自定义覆盖层
        if (_isCaptured)
          Container(
            width: double.infinity,
            height: double.infinity,
            color: Colors.black,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Image.asset("assets/whatsapp_logo.png", width: 120, height: 120),
                const SizedBox(height: 16),
                const Text("无法截图", style: TextStyle(color: Colors.white, fontSize: 18)),
              ],
            ),
          ),
      ],
    );
  }
}

内容的提问来源于stack exchange,提问作者lakhera24

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:09:51