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的自定义防截屏效果示例:
实现方案
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
相关产品推荐
相关产品推荐

