update deps

This commit is contained in:
otsmr 2026-05-01 00:09:02 +02:00
parent 7c253994e4
commit 793b7f0562
20 changed files with 440 additions and 695 deletions

View file

@ -7,10 +7,10 @@ hand_signature: 1beedb164d093643365b0832277c377353c7464f
hashlib: bc9c2f8dd7bbc72f47ccab0ce1111d40259c49bc
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
libsignal_protocol_dart: 618f0c0b49534245a640a31d204265440cbac9ee
libsignal_protocol_dart: c95a1586057022acdbb9c76b1692d94cc549bcc7
lottie: 4f1a5a52bdf1e1c1e12fa97c96174dcb05419e19
mutex: 84ca903a3ac863735e3228c75a212133621f680f
no_screenshot: 1b561a2a87c19e317f3cb225ed023b113c9dd3a1
no_screenshot: daf759e30219224630b4af0b82061d25a457a393
optional: 71c638891ce4f2aff35c7387727989f31f9d877d
photo_view: a13ca2fc387a3fb1276126959e092c44d0029987
pointycastle: bbd8569f68a7fccbdf0b92d0b44a9219c126c8dd

View file

@ -70,7 +70,7 @@ class NumericFingerprintGenerator implements FingerprintGenerator {
final sortedIdentityKeys = [...identityKeys]..sort(identityKeyComparator);
final keys = <int>[];
sortedIdentityKeys.forEach((IdentityKey key) {
sortedIdentityKeys.forEach((key) {
final publicKeyBytes = key.publicKey.serialize();
keys.addAll(publicKeyBytes.toList());
});

View file

@ -62,9 +62,6 @@ class LogicalFingerprint extends $pb.GeneratedMessage {
@override
LogicalFingerprint createEmptyInstance() => create();
static $pb.PbList<LogicalFingerprint> createRepeated() =>
$pb.PbList<LogicalFingerprint>();
@$core.pragma('dart2js:noInline')
static LogicalFingerprint getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<LogicalFingerprint>(create);
@ -167,9 +164,6 @@ class CombinedFingerprints extends $pb.GeneratedMessage {
@override
CombinedFingerprints createEmptyInstance() => create();
static $pb.PbList<CombinedFingerprints> createRepeated() =>
$pb.PbList<CombinedFingerprints>();
@$core.pragma('dart2js:noInline')
static CombinedFingerprints getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<CombinedFingerprints>(create);

View file

@ -80,9 +80,6 @@ class SessionStructureChainChainKey extends $pb.GeneratedMessage {
@override
SessionStructureChainChainKey createEmptyInstance() => create();
static $pb.PbList<SessionStructureChainChainKey> createRepeated() =>
$pb.PbList<SessionStructureChainChainKey>();
@$core.pragma('dart2js:noInline')
static SessionStructureChainChainKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChainChainKey>(create);
@ -214,9 +211,6 @@ class SessionStructureChainMessageKey extends $pb.GeneratedMessage {
@override
SessionStructureChainMessageKey createEmptyInstance() => create();
static $pb.PbList<SessionStructureChainMessageKey> createRepeated() =>
$pb.PbList<SessionStructureChainMessageKey>();
@$core.pragma('dart2js:noInline')
static SessionStructureChainMessageKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChainMessageKey>(
@ -378,9 +372,6 @@ class SessionStructureChain extends $pb.GeneratedMessage {
@override
SessionStructureChain createEmptyInstance() => create();
static $pb.PbList<SessionStructureChain> createRepeated() =>
$pb.PbList<SessionStructureChain>();
@$core.pragma('dart2js:noInline')
static SessionStructureChain getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChain>(create);
@ -565,9 +556,6 @@ class SessionStructurePendingKeyExchange extends $pb.GeneratedMessage {
@override
SessionStructurePendingKeyExchange createEmptyInstance() => create();
static $pb.PbList<SessionStructurePendingKeyExchange> createRepeated() =>
$pb.PbList<SessionStructurePendingKeyExchange>();
@$core.pragma('dart2js:noInline')
static SessionStructurePendingKeyExchange getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructurePendingKeyExchange>(
@ -760,9 +748,6 @@ class SessionStructurePendingPreKey extends $pb.GeneratedMessage {
@override
SessionStructurePendingPreKey createEmptyInstance() => create();
static $pb.PbList<SessionStructurePendingPreKey> createRepeated() =>
$pb.PbList<SessionStructurePendingPreKey>();
@$core.pragma('dart2js:noInline')
static SessionStructurePendingPreKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructurePendingPreKey>(create);
@ -1004,9 +989,6 @@ class SessionStructure extends $pb.GeneratedMessage {
@override
SessionStructure createEmptyInstance() => create();
static $pb.PbList<SessionStructure> createRepeated() =>
$pb.PbList<SessionStructure>();
@$core.pragma('dart2js:noInline')
static SessionStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructure>(create);
@ -1266,9 +1248,6 @@ class RecordStructure extends $pb.GeneratedMessage {
@override
RecordStructure createEmptyInstance() => create();
static $pb.PbList<RecordStructure> createRepeated() =>
$pb.PbList<RecordStructure>();
@$core.pragma('dart2js:noInline')
static RecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<RecordStructure>(create);
@ -1379,9 +1358,6 @@ class PreKeyRecordStructure extends $pb.GeneratedMessage {
@override
PreKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<PreKeyRecordStructure> createRepeated() =>
$pb.PbList<PreKeyRecordStructure>();
@$core.pragma('dart2js:noInline')
static PreKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreKeyRecordStructure>(create);
@ -1537,9 +1513,6 @@ class SignedPreKeyRecordStructure extends $pb.GeneratedMessage {
@override
SignedPreKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<SignedPreKeyRecordStructure> createRepeated() =>
$pb.PbList<SignedPreKeyRecordStructure>();
@$core.pragma('dart2js:noInline')
static SignedPreKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SignedPreKeyRecordStructure>(create);
@ -1690,9 +1663,6 @@ class IdentityKeyPairStructure extends $pb.GeneratedMessage {
@override
IdentityKeyPairStructure createEmptyInstance() => create();
static $pb.PbList<IdentityKeyPairStructure> createRepeated() =>
$pb.PbList<IdentityKeyPairStructure>();
@$core.pragma('dart2js:noInline')
static IdentityKeyPairStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<IdentityKeyPairStructure>(create);
@ -1802,9 +1772,6 @@ class SenderKeyStateStructureSenderChainKey extends $pb.GeneratedMessage {
@override
SenderKeyStateStructureSenderChainKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderChainKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderChainKey>();
@$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderChainKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -1915,9 +1882,6 @@ class SenderKeyStateStructureSenderMessageKey extends $pb.GeneratedMessage {
@override
SenderKeyStateStructureSenderMessageKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderMessageKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderMessageKey>();
@$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderMessageKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -2027,9 +1991,6 @@ class SenderKeyStateStructureSenderSigningKey extends $pb.GeneratedMessage {
@override
SenderKeyStateStructureSenderSigningKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderSigningKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderSigningKey>();
@$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderSigningKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -2162,9 +2123,6 @@ class SenderKeyStateStructure extends $pb.GeneratedMessage {
@override
SenderKeyStateStructure createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructure> createRepeated() =>
$pb.PbList<SenderKeyStateStructure>();
@$core.pragma('dart2js:noInline')
static SenderKeyStateStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyStateStructure>(create);
@ -2288,9 +2246,6 @@ class SenderKeyRecordStructure extends $pb.GeneratedMessage {
@override
SenderKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<SenderKeyRecordStructure> createRepeated() =>
$pb.PbList<SenderKeyRecordStructure>();
@$core.pragma('dart2js:noInline')
static SenderKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyRecordStructure>(create);

View file

@ -95,9 +95,6 @@ class SignalMessage extends $pb.GeneratedMessage {
@override
SignalMessage createEmptyInstance() => create();
static $pb.PbList<SignalMessage> createRepeated() =>
$pb.PbList<SignalMessage>();
@$core.pragma('dart2js:noInline')
static SignalMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SignalMessage>(create);
@ -275,9 +272,6 @@ class PreKeySignalMessage extends $pb.GeneratedMessage {
@override
PreKeySignalMessage createEmptyInstance() => create();
static $pb.PbList<PreKeySignalMessage> createRepeated() =>
$pb.PbList<PreKeySignalMessage>();
@$core.pragma('dart2js:noInline')
static PreKeySignalMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreKeySignalMessage>(create);
@ -473,9 +467,6 @@ class KeyExchangeMessage extends $pb.GeneratedMessage {
@override
KeyExchangeMessage createEmptyInstance() => create();
static $pb.PbList<KeyExchangeMessage> createRepeated() =>
$pb.PbList<KeyExchangeMessage>();
@$core.pragma('dart2js:noInline')
static KeyExchangeMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<KeyExchangeMessage>(create);
@ -632,9 +623,6 @@ class SenderKeyMessage extends $pb.GeneratedMessage {
@override
SenderKeyMessage createEmptyInstance() => create();
static $pb.PbList<SenderKeyMessage> createRepeated() =>
$pb.PbList<SenderKeyMessage>();
@$core.pragma('dart2js:noInline')
static SenderKeyMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyMessage>(create);
@ -779,9 +767,6 @@ class SenderKeyDistributionMessage extends $pb.GeneratedMessage {
@override
SenderKeyDistributionMessage createEmptyInstance() => create();
static $pb.PbList<SenderKeyDistributionMessage> createRepeated() =>
$pb.PbList<SenderKeyDistributionMessage>();
@$core.pragma('dart2js:noInline')
static SenderKeyDistributionMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyDistributionMessage>(create);
@ -918,9 +903,6 @@ class DeviceConsistencyCodeMessage extends $pb.GeneratedMessage {
@override
DeviceConsistencyCodeMessage createEmptyInstance() => create();
static $pb.PbList<DeviceConsistencyCodeMessage> createRepeated() =>
$pb.PbList<DeviceConsistencyCodeMessage>();
@$core.pragma('dart2js:noInline')
static DeviceConsistencyCodeMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<DeviceConsistencyCodeMessage>(create);

View file

@ -72,7 +72,6 @@ class ByteUtil {
}
static int compare(Uint8List left, Uint8List right) {
// ignore: parameter_assignments, avoid_multiple_declarations_per_line
for (var i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
final a = left[i] & 0xff;
final b = right[j] & 0xff;

View file

@ -1,6 +1,6 @@
name: libsignal_protocol_dart
description: Signal Protocol libray for Dart native and Flutter, pure Dart implementation of the the Signal Protocol
version: 0.7.4
version: 0.8.0
repository: https://github.com/MixinNetwork/libsignal_protocol_dart
topics:
- crypto
@ -19,10 +19,10 @@ dependencies:
meta: ^1.16.0
optional: ^6.1.0+1
pointycastle: ^4.0.0
protobuf: ^4.0.0
protobuf: ^6.0.0
x25519: ^0.1.1
dev_dependencies:
test: ^1.25.8
very_good_analysis: ^8.0.0
very_good_analysis: ^10.2.0

View file

@ -22,7 +22,6 @@ import android.renderscript.ScriptIntrinsicBlur
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams
import android.widget.FrameLayout
import android.widget.ImageView
@ -88,7 +87,6 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
private var isScreenRecording: Boolean = false
private var isRecordingListening: Boolean = false
private var screenCaptureCallback: Any? = null
private var screenRecordingCallback: Any? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
@ -114,12 +112,12 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
activity = binding.activity
restoreScreenshotState()
if (isRecordingListening) {
registerScreenRecordingCallbacks()
registerScreenCaptureCallback()
}
}
override fun onDetachedFromActivityForConfigChanges() {
unregisterScreenRecordingCallbacks()
unregisterScreenCaptureCallback()
removeImageOverlay()
removeBlurOverlay()
removeColorOverlay()
@ -130,12 +128,12 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
activity = binding.activity
restoreScreenshotState()
if (isRecordingListening) {
registerScreenRecordingCallbacks()
registerScreenCaptureCallback()
}
}
override fun onDetachedFromActivity() {
unregisterScreenRecordingCallbacks()
unregisterScreenCaptureCallback()
removeImageOverlay()
removeBlurOverlay()
removeColorOverlay()
@ -273,7 +271,7 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
private fun showImageOverlay(activity: Activity) {
if (overlayImageView != null) return
val resId = activity.resources.getIdentifier("no_screenshot_image", "drawable", activity.packageName)
val resId = activity.resources.getIdentifier("image", "drawable", activity.packageName)
if (resId == 0) return
activity.runOnUiThread {
val imageView = ImageView(activity).apply {
@ -546,77 +544,24 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
}
// ── Screen Recording Detection ─────────────────────────────────────
//
// API 35+ (Android 15): WindowManager.addScreenRecordingCallback
// → true start/stop detection via SCREEN_RECORDING_STATE_VISIBLE / _NOT_VISIBLE
// API 34 (Android 14): Activity.ScreenCaptureCallback
// → fires on screen capture (start only, no stop event)
// API <34: graceful no-op
private fun startRecordingListening() {
if (isRecordingListening) return
isRecordingListening = true
registerScreenRecordingCallbacks()
registerScreenCaptureCallback()
updateSharedPreferencesState("")
}
private fun stopRecordingListening() {
if (!isRecordingListening) return
isRecordingListening = false
unregisterScreenRecordingCallbacks()
unregisterScreenCaptureCallback()
isScreenRecording = false
updateSharedPreferencesState("")
}
private fun registerScreenRecordingCallbacks() {
if (Build.VERSION.SDK_INT >= 35) {
registerScreenRecordingCallback()
} else if (Build.VERSION.SDK_INT >= 34) {
registerScreenCaptureCallback()
}
}
private fun unregisterScreenRecordingCallbacks() {
if (Build.VERSION.SDK_INT >= 35) {
unregisterScreenRecordingCallback()
}
if (Build.VERSION.SDK_INT >= 34) {
unregisterScreenCaptureCallback()
}
}
@Suppress("NewApi")
private fun registerScreenRecordingCallback() {
val act = activity ?: return
if (screenRecordingCallback != null) return
val callback = java.util.function.Consumer<Int> { state ->
val wasRecording = isScreenRecording
isScreenRecording = (state == WindowManager.SCREEN_RECORDING_STATE_VISIBLE)
if (isScreenRecording != wasRecording) {
updateSharedPreferencesState("", System.currentTimeMillis())
}
}
val initialState = act.windowManager.addScreenRecordingCallback(act.mainExecutor, callback)
screenRecordingCallback = callback
// Process initial state
val wasRecording = isScreenRecording
isScreenRecording = (initialState == WindowManager.SCREEN_RECORDING_STATE_VISIBLE)
if (isScreenRecording != wasRecording) {
updateSharedPreferencesState("", System.currentTimeMillis())
}
}
@Suppress("NewApi", "UNCHECKED_CAST")
private fun unregisterScreenRecordingCallback() {
val act = activity ?: return
val callback = screenRecordingCallback as? java.util.function.Consumer<Int> ?: return
act.windowManager.removeScreenRecordingCallback(callback)
screenRecordingCallback = null
}
private fun registerScreenCaptureCallback() {
if (Build.VERSION.SDK_INT >= 34) {
if (android.os.Build.VERSION.SDK_INT >= 34) {
val act = activity ?: return
if (screenCaptureCallback != null) return
@ -630,7 +575,7 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
}
private fun unregisterScreenCaptureCallback() {
if (Build.VERSION.SDK_INT >= 34) {
if (android.os.Build.VERSION.SDK_INT >= 34) {
val act = activity ?: return
val callback = screenCaptureCallback as? Activity.ScreenCaptureCallback ?: return
act.unregisterScreenCaptureCallback(callback)

View file

@ -138,7 +138,7 @@ public class IOSNoScreenshotPlugin: NSObject, FlutterPlugin, FlutterStreamHandle
// visible in the app switcher (otherwise the secure text field
// would show a blank screen).
disablePreventScreenshot()
enableImageScreen(named: "NoScreenshotImage")
enableImageScreen(named: "image")
} else if isBlurOverlayModeEnabled {
disablePreventScreenshot()
enableBlurScreen(radius: blurRadius)

View file

@ -14,8 +14,7 @@ class NoScreenshot implements NoScreenshotPlatform {
NoScreenshot._();
@Deprecated(
"Using this may cause issue\nUse instance directly\ne.g: 'NoScreenshot.instance.screenshotOff()'",
)
"Using this may cause issue\nUse instance directly\ne.g: 'NoScreenshot.instance.screenshotOff()'")
NoScreenshot();
static final NoScreenshot instance = NoScreenshot._();

View file

@ -19,18 +19,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override
Stream<ScreenshotSnapshot> get screenshotStream {
_cachedStream ??= eventChannel.receiveBroadcastStream().map(
(event) =>
ScreenshotSnapshot.fromMap(jsonDecode(event) as Map<String, dynamic>),
);
_cachedStream ??= eventChannel.receiveBroadcastStream().map((event) =>
ScreenshotSnapshot.fromMap(jsonDecode(event) as Map<String, dynamic>));
return _cachedStream!;
}
@override
Future<bool> toggleScreenshot() async {
final result = await methodChannel.invokeMethod<bool>(
toggleScreenShotConst,
);
final result =
await methodChannel.invokeMethod<bool>(toggleScreenShotConst);
return result ?? false;
}
@ -54,17 +51,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override
Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) async {
final result = await methodChannel.invokeMethod<bool>(screenSetBlur, {
'radius': blurRadius,
});
final result = await methodChannel
.invokeMethod<bool>(screenSetBlur, {'radius': blurRadius});
return result ?? false;
}
@override
Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) async {
final result = await methodChannel.invokeMethod<bool>(screenSetColor, {
'color': color,
});
final result = await methodChannel
.invokeMethod<bool>(screenSetColor, {'color': color});
return result ?? false;
}
@ -76,17 +71,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override
Future<bool> screenshotWithBlur({double blurRadius = 30.0}) async {
final result = await methodChannel.invokeMethod<bool>(screenEnableBlur, {
'radius': blurRadius,
});
final result = await methodChannel
.invokeMethod<bool>(screenEnableBlur, {'radius': blurRadius});
return result ?? false;
}
@override
Future<bool> screenshotWithColor({int color = 0xFF000000}) async {
final result = await methodChannel.invokeMethod<bool>(screenEnableColor, {
'color': color,
});
final result = await methodChannel
.invokeMethod<bool>(screenEnableColor, {'color': color});
return result ?? false;
}

View file

@ -43,20 +43,17 @@ abstract class NoScreenshotPlatform extends PlatformInterface {
/// throw `UnmimplementedError` if not implement
Future<bool> toggleScreenshotWithImage() {
throw UnimplementedError(
'toggleScreenshotWithImage() has not been implemented.',
);
'toggleScreenshotWithImage() has not been implemented.');
}
Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) {
throw UnimplementedError(
'toggleScreenshotWithBlur() has not been implemented.',
);
'toggleScreenshotWithBlur() has not been implemented.');
}
Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) {
throw UnimplementedError(
'toggleScreenshotWithColor() has not been implemented.',
);
'toggleScreenshotWithColor() has not been implemented.');
}
/// Always enables image overlay mode (idempotent safe to call repeatedly).
@ -90,31 +87,27 @@ abstract class NoScreenshotPlatform extends PlatformInterface {
throw UnimplementedError('incrementStream has not been implemented.');
}
// Start listening to screenshot activities
// Start listening to screenshot activities
Future<void> startScreenshotListening() {
throw UnimplementedError(
'startScreenshotListening has not been implemented.',
);
'startScreenshotListening has not been implemented.');
}
/// Stop listening to screenshot activities
Future<void> stopScreenshotListening() {
throw UnimplementedError(
'stopScreenshotListening has not been implemented.',
);
'stopScreenshotListening has not been implemented.');
}
/// Start listening to screen recording activities
Future<void> startScreenRecordingListening() {
throw UnimplementedError(
'startScreenRecordingListening has not been implemented.',
);
'startScreenRecordingListening has not been implemented.');
}
/// Stop listening to screen recording activities
Future<void> stopScreenRecordingListening() {
throw UnimplementedError(
'stopScreenRecordingListening has not been implemented.',
);
'stopScreenRecordingListening has not been implemented.');
}
}

View file

@ -138,13 +138,11 @@ class NoScreenshotWeb extends NoScreenshotPlatform {
}
void _emitState({bool wasScreenshotTaken = false}) {
_controller.add(
ScreenshotSnapshot(
_controller.add(ScreenshotSnapshot(
screenshotPath: '',
isScreenshotProtectionOn: _isProtectionOn,
wasScreenshotTaken: wasScreenshotTaken,
),
);
));
}
// Context menu blocker

View file

@ -73,10 +73,7 @@ class SecureNavigatorObserver extends NavigatorObserver {
void _applyPolicyForRoute(Route<dynamic>? route) {
final name = route?.settings.name;
final config = (name != null ? policies[name] : null) ?? defaultConfig;
applyOverlayMode(
config.mode,
blurRadius: config.blurRadius,
color: config.color,
);
applyOverlayMode(config.mode,
blurRadius: config.blurRadius, color: config.color);
}
}

View file

@ -1,6 +1,6 @@
name: no_screenshot
description: Flutter plugin to prevent screenshots, detect screen recording, and show blur/color/image overlays in the app switcher on Android, iOS, macOS, Linux, Windows, and Web.
version: 1.1.0
version: 0.10.0
homepage: https://flutterplaza.com
repository: https://github.com/FlutterPlaza/no_screenshot

View file

@ -133,8 +133,7 @@ void main() {
expect(result, expected);
});
test(
'toggleScreenshotWithBlur returns false when channel returns null',
test('toggleScreenshotWithBlur returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -143,8 +142,7 @@ void main() {
final result = await platform.toggleScreenshotWithBlur();
expect(result, false);
},
);
});
test('toggleScreenshotWithColor', () async {
const bool expected = true;
@ -172,14 +170,12 @@ void main() {
return null;
});
final result = await platform.toggleScreenshotWithColor(
color: 0xFFFF0000,
);
final result =
await platform.toggleScreenshotWithColor(color: 0xFFFF0000);
expect(result, expected);
});
test(
'toggleScreenshotWithColor returns false when channel returns null',
test('toggleScreenshotWithColor returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -188,11 +184,9 @@ void main() {
final result = await platform.toggleScreenshotWithColor();
expect(result, false);
},
);
});
test(
'toggleScreenshotWithImage returns false when channel returns null',
test('toggleScreenshotWithImage returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -201,8 +195,7 @@ void main() {
final result = await platform.toggleScreenshotWithImage();
expect(result, false);
},
);
});
test('screenshotOn returns false when channel returns null', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@ -248,8 +241,7 @@ void main() {
expect(result, expected);
});
test(
'screenshotWithImage returns false when channel returns null',
test('screenshotWithImage returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -258,8 +250,7 @@ void main() {
final result = await platform.screenshotWithImage();
expect(result, false);
},
);
});
test('screenshotWithBlur', () async {
const bool expected = true;
@ -291,8 +282,7 @@ void main() {
expect(result, expected);
});
test(
'screenshotWithBlur returns false when channel returns null',
test('screenshotWithBlur returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -301,8 +291,7 @@ void main() {
final result = await platform.screenshotWithBlur();
expect(result, false);
},
);
});
test('screenshotWithColor', () async {
const bool expected = true;
@ -334,8 +323,7 @@ void main() {
expect(result, expected);
});
test(
'screenshotWithColor returns false when channel returns null',
test('screenshotWithColor returns false when channel returns null',
() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -344,8 +332,7 @@ void main() {
final result = await platform.screenshotWithColor();
expect(result, false);
},
);
});
test('startScreenRecordingListening', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@ -373,8 +360,7 @@ void main() {
expect(true, true);
});
test(
'screenshotStream returns a stream that emits ScreenshotSnapshot',
test('screenshotStream returns a stream that emits ScreenshotSnapshot',
() async {
final snapshotMap = {
'screenshot_path': '/test/path',
@ -388,14 +374,11 @@ void main() {
// Mock the event channel by handling the underlying method channel
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockStreamHandler(
platform.eventChannel,
MockStreamHandler.inline(
.setMockStreamHandler(platform.eventChannel, MockStreamHandler.inline(
onListen: (arguments, events) {
events.success(encoded);
},
),
);
));
final stream = platform.screenshotStream;
final snapshot = await stream.first;
@ -403,8 +386,7 @@ void main() {
expect(snapshot.screenshotPath, '/test/path');
expect(snapshot.isScreenshotProtectionOn, true);
expect(snapshot.wasScreenshotTaken, true);
},
);
});
test('screenshotStream caches and returns the same stream instance', () {
final stream1 = platform.screenshotStream;
@ -635,10 +617,8 @@ void main() {
wasScreenshotTaken: true,
);
final string = snapshot.toString();
expect(
string,
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: false, \ntimestamp: 0, \nsourceApp: \n)',
);
expect(string,
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: false, \ntimestamp: 0, \nsourceApp: \n)');
});
test('toString with isScreenRecording true', () {
@ -649,10 +629,8 @@ void main() {
isScreenRecording: true,
);
final string = snapshot.toString();
expect(
string,
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: true, \ntimestamp: 0, \nsourceApp: \n)',
);
expect(string,
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: true, \ntimestamp: 0, \nsourceApp: \n)');
});
test('toString with metadata', () {
@ -688,46 +666,38 @@ void main() {
controller.close();
});
test(
'onScreenshotDetected fires when wasScreenshotTaken is true',
test('onScreenshotDetected fires when wasScreenshotTaken is true',
() async {
final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add;
noScreenshot.startCallbacks();
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '/path',
isScreenshotProtectionOn: true,
wasScreenshotTaken: true,
),
);
));
await Future.delayed(Duration.zero);
expect(detected, hasLength(1));
expect(detected.first.wasScreenshotTaken, true);
},
);
});
test(
'onScreenshotDetected does NOT fire when wasScreenshotTaken is false',
test('onScreenshotDetected does NOT fire when wasScreenshotTaken is false',
() async {
final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add;
noScreenshot.startCallbacks();
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '',
isScreenshotProtectionOn: true,
wasScreenshotTaken: false,
),
);
));
await Future.delayed(Duration.zero);
expect(detected, isEmpty);
},
);
});
test('onScreenRecordingStarted fires on false→true transition', () async {
final started = <ScreenshotSnapshot>[];
@ -735,14 +705,12 @@ void main() {
noScreenshot.startCallbacks();
// Initial state: not recording recording starts
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '',
isScreenshotProtectionOn: true,
wasScreenshotTaken: false,
isScreenRecording: true,
),
);
));
await Future.delayed(Duration.zero);
expect(started, hasLength(1));
@ -755,33 +723,28 @@ void main() {
noScreenshot.startCallbacks();
// First: recording starts
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '',
isScreenshotProtectionOn: true,
wasScreenshotTaken: false,
isScreenRecording: true,
),
);
));
await Future.delayed(Duration.zero);
// Then: recording stops
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '',
isScreenshotProtectionOn: true,
wasScreenshotTaken: false,
isScreenRecording: false,
),
);
));
await Future.delayed(Duration.zero);
expect(stopped, hasLength(1));
expect(stopped.first.isScreenRecording, false);
});
test(
'removeAllCallbacks clears all callbacks and stops subscription',
test('removeAllCallbacks clears all callbacks and stops subscription',
() async {
final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add;
@ -795,18 +758,15 @@ void main() {
expect(noScreenshot.onScreenRecordingStopped, isNull);
// Events after removal should not fire
controller.add(
ScreenshotSnapshot(
controller.add(ScreenshotSnapshot(
screenshotPath: '/path',
isScreenshotProtectionOn: true,
wasScreenshotTaken: true,
),
);
));
await Future.delayed(Duration.zero);
expect(detected, isEmpty);
},
);
});
test('hasActiveCallbacks reflects subscription state', () {
expect(noScreenshot.hasActiveCallbacks, false);

View file

@ -84,10 +84,8 @@ void main() {
group('NoScreenshotPlatform', () {
test('default instance should be MethodChannelNoScreenshot', () {
expect(
NoScreenshotPlatform.instance,
isInstanceOf<MethodChannelNoScreenshot>(),
);
expect(NoScreenshotPlatform.instance,
isInstanceOf<MethodChannelNoScreenshot>());
});
test('screenshotOff should return true when called', () async {
@ -102,28 +100,21 @@ void main() {
expect(await platform.toggleScreenshot(), isTrue);
});
test(
'screenshotStream should not throw UnimplementedError when accessed',
test('screenshotStream should not throw UnimplementedError when accessed',
() {
expect(
() => platform.screenshotStream,
isNot(throwsUnimplementedError),
);
},
);
expect(() => platform.screenshotStream, isNot(throwsUnimplementedError));
});
test(
'startScreenshotListening should not throw UnimplementedError when called',
() async {
expect(platform.startScreenshotListening(), completes);
},
);
});
test(
'stopScreenshotListening should not throw UnimplementedError when called',
() async {
expect(platform.stopScreenshotListening(), completes);
},
);
});
test('toggleScreenshotWithImage should return true when called', () async {
expect(await platform.toggleScreenshotWithImage(), isTrue);
@ -133,12 +124,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithImage() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.toggleScreenshotWithImage(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.toggleScreenshotWithImage(),
throwsUnimplementedError);
});
test('toggleScreenshotWithBlur should return true when called', () async {
expect(await platform.toggleScreenshotWithBlur(), isTrue);
@ -148,12 +136,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithBlur() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.toggleScreenshotWithBlur(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.toggleScreenshotWithBlur(),
throwsUnimplementedError);
});
test('toggleScreenshotWithColor should return true when called', () async {
expect(await platform.toggleScreenshotWithColor(), isTrue);
@ -163,12 +148,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithColor() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.toggleScreenshotWithColor(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.toggleScreenshotWithColor(),
throwsUnimplementedError);
});
test('screenshotWithImage should return true when called', () async {
expect(await platform.screenshotWithImage(), isTrue);
@ -179,11 +161,8 @@ void main() {
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.screenshotWithImage(),
throwsUnimplementedError,
);
},
);
() => basePlatform.screenshotWithImage(), throwsUnimplementedError);
});
test('screenshotWithBlur should return true when called', () async {
expect(await platform.screenshotWithBlur(), isTrue);
@ -193,12 +172,8 @@ void main() {
'base NoScreenshotPlatform.screenshotWithBlur() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.screenshotWithBlur(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.screenshotWithBlur(), throwsUnimplementedError);
});
test('screenshotWithColor should return true when called', () async {
expect(await platform.screenshotWithColor(), isTrue);
@ -209,100 +184,76 @@ void main() {
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.screenshotWithColor(),
throwsUnimplementedError,
);
},
);
() => basePlatform.screenshotWithColor(), throwsUnimplementedError);
});
test(
'base NoScreenshotPlatform.screenshotOff() throws UnimplementedError',
test('base NoScreenshotPlatform.screenshotOff() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotOff(), throwsUnimplementedError);
},
);
});
test(
'base NoScreenshotPlatform.screenshotOn() throws UnimplementedError',
test('base NoScreenshotPlatform.screenshotOn() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotOn(), throwsUnimplementedError);
},
);
});
test(
'base NoScreenshotPlatform.toggleScreenshot() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.toggleScreenshot(), throwsUnimplementedError);
},
);
});
test(
'base NoScreenshotPlatform.screenshotStream throws UnimplementedError',
test('base NoScreenshotPlatform.screenshotStream throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotStream, throwsUnimplementedError);
},
);
});
test(
'base NoScreenshotPlatform.startScreenshotListening() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.startScreenshotListening(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.startScreenshotListening(),
throwsUnimplementedError);
});
test(
'base NoScreenshotPlatform.stopScreenshotListening() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.stopScreenshotListening(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.stopScreenshotListening(),
throwsUnimplementedError);
});
test(
'startScreenRecordingListening should not throw UnimplementedError when called',
() async {
expect(platform.startScreenRecordingListening(), completes);
},
);
});
test(
'stopScreenRecordingListening should not throw UnimplementedError when called',
() async {
expect(platform.stopScreenRecordingListening(), completes);
},
);
});
test(
'base NoScreenshotPlatform.startScreenRecordingListening() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.startScreenRecordingListening(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.startScreenRecordingListening(),
throwsUnimplementedError);
});
test(
'base NoScreenshotPlatform.stopScreenRecordingListening() throws UnimplementedError',
() {
final basePlatform = BaseNoScreenshotPlatform();
expect(
() => basePlatform.stopScreenRecordingListening(),
throwsUnimplementedError,
);
},
);
expect(() => basePlatform.stopScreenRecordingListening(),
throwsUnimplementedError);
});
});
}

View file

@ -115,10 +115,8 @@ void main() {
});
test('screenshotStream', () async {
expect(
NoScreenshot.instance.screenshotStream,
isInstanceOf<Stream<ScreenshotSnapshot>>(),
);
expect(NoScreenshot.instance.screenshotStream,
isInstanceOf<Stream<ScreenshotSnapshot>>());
});
test('startScreenshotListening', () async {
expect(NoScreenshot.instance.startScreenshotListening(), completes);
@ -139,8 +137,7 @@ void main() {
test('toggleScreenshotWithBlur with custom radius', () async {
expect(
await NoScreenshot.instance.toggleScreenshotWithBlur(blurRadius: 50.0),
true,
);
true);
});
test('toggleScreenshotWithColor', () async {
@ -149,9 +146,9 @@ void main() {
test('toggleScreenshotWithColor with custom color', () async {
expect(
await NoScreenshot.instance.toggleScreenshotWithColor(color: 0xFFFF0000),
true,
);
await NoScreenshot.instance
.toggleScreenshotWithColor(color: 0xFFFF0000),
true);
});
test('screenshotWithImage', () async {
@ -164,9 +161,7 @@ void main() {
test('screenshotWithBlur with custom radius', () async {
expect(
await NoScreenshot.instance.screenshotWithBlur(blurRadius: 50.0),
true,
);
await NoScreenshot.instance.screenshotWithBlur(blurRadius: 50.0), true);
});
test('screenshotWithColor', () async {
@ -174,10 +169,8 @@ void main() {
});
test('screenshotWithColor with custom color', () async {
expect(
await NoScreenshot.instance.screenshotWithColor(color: 0xFFFF0000),
true,
);
expect(await NoScreenshot.instance.screenshotWithColor(color: 0xFFFF0000),
true);
});
test('NoScreenshot equality operator', () {

View file

@ -132,7 +132,9 @@ void main() {
test('didPop applies policy for previous route', () async {
final observer = SecureNavigatorObserver(
policies: {'/home': const SecureRouteConfig(mode: OverlayMode.none)},
policies: {
'/home': const SecureRouteConfig(mode: OverlayMode.none),
},
);
observer.didPop(_fakeRoute('/payment'), _fakeRoute('/home'));
@ -143,24 +145,22 @@ void main() {
test('didReplace applies policy for new route', () async {
final observer = SecureNavigatorObserver(
policies: {
'/profile': const SecureRouteConfig(
mode: OverlayMode.blur,
blurRadius: 50.0,
),
'/profile':
const SecureRouteConfig(mode: OverlayMode.blur, blurRadius: 50.0),
},
);
observer.didReplace(
newRoute: _fakeRoute('/profile'),
oldRoute: _fakeRoute('/home'),
);
newRoute: _fakeRoute('/profile'), oldRoute: _fakeRoute('/home'));
await Future<void>.delayed(Duration.zero);
expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)'));
});
test('didRemove applies policy for previous route', () async {
final observer = SecureNavigatorObserver(
policies: {'/home': const SecureRouteConfig(mode: OverlayMode.none)},
policies: {
'/home': const SecureRouteConfig(mode: OverlayMode.none),
},
);
observer.didRemove(_fakeRoute('/payment'), _fakeRoute('/home'));
@ -232,9 +232,7 @@ void main() {
observer.didPush(_fakeRoute('/branded'), null);
await Future<void>.delayed(Duration.zero);
expect(
fakePlatform.calls,
contains('screenshotWithColor(${0xFF2196F3})'),
);
fakePlatform.calls, contains('screenshotWithColor(${0xFF2196F3})'));
});
});
}

View file

@ -85,14 +85,15 @@ void main() {
});
testWidgets('default mode is OverlayMode.secure', (tester) async {
await tester.pumpWidget(const SecureWidget(child: SizedBox()));
await tester.pumpWidget(
const SecureWidget(child: SizedBox()),
);
await tester.pump();
expect(fakePlatform.calls, contains('screenshotOff'));
});
testWidgets('initState calls screenshotOff for OverlayMode.secure', (
tester,
) async {
testWidgets('initState calls screenshotOff for OverlayMode.secure',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.secure, child: SizedBox()),
);
@ -100,9 +101,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotOff'));
});
testWidgets('initState calls screenshotWithBlur for OverlayMode.blur', (
tester,
) async {
testWidgets('initState calls screenshotWithBlur for OverlayMode.blur',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.blur, child: SizedBox()),
);
@ -110,9 +110,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)'));
});
testWidgets('initState calls screenshotWithColor for OverlayMode.color', (
tester,
) async {
testWidgets('initState calls screenshotWithColor for OverlayMode.color',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.color, child: SizedBox()),
);
@ -120,9 +119,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithColor(4278190080)'));
});
testWidgets('initState calls screenshotWithImage for OverlayMode.image', (
tester,
) async {
testWidgets('initState calls screenshotWithImage for OverlayMode.image',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.image, child: SizedBox()),
);
@ -130,9 +128,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithImage'));
});
testWidgets('initState calls screenshotOn for OverlayMode.none', (
tester,
) async {
testWidgets('initState calls screenshotOn for OverlayMode.none',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.none, child: SizedBox()),
);
@ -141,7 +138,9 @@ void main() {
});
testWidgets('dispose calls screenshotOn', (tester) async {
await tester.pumpWidget(const SecureWidget(child: SizedBox()));
await tester.pumpWidget(
const SecureWidget(child: SizedBox()),
);
await tester.pump();
fakePlatform.calls.clear();
@ -165,25 +164,18 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)'));
});
testWidgets('didUpdateWidget re-applies when blurRadius changes', (
tester,
) async {
testWidgets('didUpdateWidget re-applies when blurRadius changes',
(tester) async {
await tester.pumpWidget(
const SecureWidget(
mode: OverlayMode.blur,
blurRadius: 30.0,
child: SizedBox(),
),
mode: OverlayMode.blur, blurRadius: 30.0, child: SizedBox()),
);
await tester.pump();
fakePlatform.calls.clear();
await tester.pumpWidget(
const SecureWidget(
mode: OverlayMode.blur,
blurRadius: 50.0,
child: SizedBox(),
),
mode: OverlayMode.blur, blurRadius: 50.0, child: SizedBox()),
);
await tester.pump();
expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)'));
@ -198,18 +190,14 @@ void main() {
await tester.pumpWidget(
const SecureWidget(
mode: OverlayMode.color,
color: 0xFFFF0000,
child: SizedBox(),
),
mode: OverlayMode.color, color: 0xFFFF0000, child: SizedBox()),
);
await tester.pump();
expect(fakePlatform.calls, contains('screenshotWithColor(4294901760)'));
});
testWidgets('didUpdateWidget does not re-apply when nothing changes', (
tester,
) async {
testWidgets('didUpdateWidget does not re-apply when nothing changes',
(tester) async {
await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.secure, child: SizedBox()),
);