Flutter how to override System Status bar and protect App from screenshots in Android/iOS

Andongwisye Mwamengo - Sep 3 - - Dev Community

When developing mobile applications, ensuring that sensitive information displayed on the screen is protected from unauthorized screenshots or screen recordings can be critical. Flutter provides the flexibility to achieve this across both Android and iOS platforms. Below, we'll walk through how to override the system status bar and implement screenshot protection in a Flutter app.

First, ensure your Flutter project is set up with the necessary dependencies. In this example, we'll focus on two key tasks:

Overriding the System Status Bar: Customizing the appearance of the status bar and navigation bar.
Protecting the App from Screenshots: Ensuring sensitive content is not captured in screenshots or screen recordings.

Overriding the System Status Bar in Flutter
In main.dart, we will create a simple app that customizes the system UI overlays.

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

void main() => runApp(const SystemOverlayStyleApp());

class SystemOverlayStyleApp extends StatelessWidget {
  const SystemOverlayStyleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.light,
      ),
      home: const SystemOverlayPage(),
    );
  }
}

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

  @override
  State<SystemOverlayPage> createState() => _SystemOverlayPageState();
}

class _SystemOverlayPageState extends State<SystemOverlayPage>
    with WidgetsBindingObserver {
  bool _isMinimized = false;
  final color = Colors.blue;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

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

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(() {
      if (state == AppLifecycleState.inactive ||
          state == AppLifecycleState.paused) {
        _isMinimized = true;
      } else if (state == AppLifecycleState.resumed) {
        _isMinimized = false;
      }
    });
  }

  SystemUiOverlayStyle _currentStyle = SystemUiOverlayStyle.dark.copyWith(
    statusBarColor: Colors.blue,
    systemNavigationBarColor: Colors.blue,
  );

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: _currentStyle,
      child: _isMinimized == true
          ? Container(
              color: Colors.grey,
            )
          : Scaffold(
              appBar: AppBar(
                backgroundColor: Colors.red,
                automaticallyImplyLeading: true,
                title: Text('Post'),
              ),
              body: Container(
                color: Colors.amber,
                width: double.infinity,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Text(
                        'Millions Using Dart Language',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                    ),
                  ],
                ),
              ),
              bottomNavigationBar: BottomAppBar(
                color: color.withOpacity(0.95),
                shape: const CircularNotchedRectangle(),
                notchMargin: 5.0,
                clipBehavior: Clip.antiAlias,
                child: SizedBox(
                  height: kBottomNavigationBarHeight,
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: <Widget>[
                      IconButton(
                        icon: const Icon(Icons.home),
                        onPressed: () {
                          setState(() {});
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.search),
                        onPressed: () {
                          setState(() {});
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.favorite_border_outlined),
                        onPressed: () {
                          setState(() {});
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.account_circle_outlined),
                        onPressed: () {
                          setState(() {});
                        },
                      )
                    ],
                  ),
                ),
              ),
            ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Protecting the App from Screenshots on iOS
To protect your Flutter app from screenshots on iOS, we need to override the AppDelegate.swift file. This file handles the application's lifecycle events in iOS. The goal is to prevent screen capture by using secure layers over the app's window.

import UIKit
import Flutter
import Foundation

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    self.window.makeSecure()
    GeneratedPluginRegistrant.register(with: self)
    self.window?.layer.contents = nil
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

extension UIWindow {
    func makeSecure() {
        let field = UITextField()
        let view = UIView(frame: CGRect(x: 0, y: 0, width: field.frame.width, height: field.frame.height))
        let image = UIImageView(image: UIImage(named: "whiteImage"))
        image.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        field.isSecureTextEntry = true
        if let window = self as UIWindow? {
            window.addSubview(field)
            view.addSubview(image)
            window.layer.superlayer?.addSublayer(field.layer)
            if let lastLayer = field.layer.sublayers?.last {
                lastLayer.addSublayer(window.layer)
            }
            field.leftView = view
            field.leftViewMode = .always
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Protecting the App from Screenshots on Android
For Android, we implement screenshot protection by using a flag in the MainActivity.kt file. This file is responsible for configuring the Flutter engine for Android.

import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import android.view.WindowManager.LayoutParams

class MainActivity: FlutterFragmentActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        window.addFlags(LayoutParams.FLAG_SECURE)
        GeneratedPluginRegistrant.registerWith(flutterEngine)
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary
This guide covered how to customize the system status bar in a Flutter application and protect your app from screenshots or screen recordings on both Android and iOS platforms.

For Flutter: We used SystemUiOverlayStyle to manage the appearance of the system status bar.
For iOS: We added a secure layer over the app window using Swift, making it impossible for the system to capture the screen.

For Android: We utilized the FLAG_SECURE flag to prevent screenshots or screen recordings.
By implementing these techniques, you can enhance the security of your Flutter application and ensure that sensitive data remains protected.

. . . .
Terabox Video Player