A Flutter plugin for reading the position and drawable shape of a device camera cutout on Android and iOS.
- Returns cutout bounds in Flutter logical pixels relative to the application window.
- Returns display-cutout safe insets and the current screen size.
- Returns a drawable vector path when one is available.
- Streams updated geometry after rotation and window-layout changes.
- Distinguishes notches, hole punches, Dynamic Island, and unknown shapes.
- Reports whether geometry is exact, estimated, or bounds-only.
- Supports CocoaPods and Swift Package Manager.
| Platform | Behavior |
|---|---|
| Android 12+ | Reads DisplayCutout.cutoutPath and serializes the OS path into Flutter coordinates. |
| Android 9–11 | Reads DisplayCutout.boundingRects and safe insets. |
| Android 7–8 | Returns isSupported: false without failing the application. |
| iOS | Resolves the hardware identifier against a bundled device catalog and returns estimated notch or Dynamic Island geometry. |
UIKit exposes safe areas but does not expose iPhone sensor-housing geometry. The iOS catalog covers iPhone X through iPhone 17, iPhone Air, iPhone 16e, and iPhone 17e. Known iPhones without a cutout, iPads, and iPods return a supported result with an empty cutout list. An unknown future iPhone returns isSupported: false so callers can distinguish missing catalog data from a device that has no cutout.
- Flutter 3.38.0 or later
- Dart 3.10.0 or later
- Android API 24 or later
- iOS 13.0 or later
dependencies:
camera_cutout: <latest_version>Then run:
flutter pub getimport 'dart:async';
import 'package:camera_cutout/camera_cutout.dart';
final cameraCutout = CameraCutout();
final info = await cameraCutout.getInfo();
for (final cutout in info.cutouts) {
final bounds = cutout.bounds;
final path = cutout.path?.toPath();
final kind = cutout.kind;
final accuracy = cutout.accuracy;
}
final subscription = cameraCutout.changes.listen((info) {
for (final cutout in info.cutouts) {
renderCutout(cutout.path?.toPath(), cutout.bounds);
}
});
await subscription.cancel();CameraCutoutPath.toPath() creates a dart:ui Path in screen coordinates. Use the returned path directly in a full-window CustomPainter. When path is null, draw the supplied bounds as the fallback.
void paintCutout(Canvas canvas, CameraCutoutGeometry cutout) {
final path = cutout.path?.toPath() ?? Path()..addOval(cutout.bounds);
canvas.drawPath(
path,
Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2,
);
}The plugin only reads window geometry and does not change host layout policy. An overlay that must line up with a cutout should let its activity render into the cutout area.
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes = window.attributes.apply {
layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
}
}The included example selects LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS where Android supports it and uses Flutter's edge-to-edge system UI mode.
Future<CameraCutoutInfo> getInfo()reads the current geometry.Stream<CameraCutoutInfo> get changesemits geometry after relevant native layout changes.
platformdeviceIdentifierdeviceModelscreenSizedevicePixelRatiosafeInsetsisSupportedcutoutshasCutouthasExactPath
boundskindaccuracypathhasPath
The catalog is intentionally local and deterministic. Update it when Apple releases a new iPhone identifier, verify the display family against current Apple design resources or Simulator profiles, and add the identifier to the iOS catalog before publishing a new version.
The example application draws the cutout shape at its physical screen position with a repeating glow animation and displays the returned bounds and accuracy.
![]() |
![]() |
| Android Exact OS path on a Nothing A063 |
iOS Estimated catalog geometry on an iPhone 17 Pro |
MIT

