notch 1.0.0 copy "notch: ^1.0.0" to clipboard
notch: ^1.0.0 copied to clipboard

Detect the position and shape of the device's camera cutout (notch, hole-punch, Dynamic Island) and use it as the origin of overlays and animations. Android and iOS.

example/lib/main.dart

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // Draw edge-to-edge so the cutout region belongs to the app.
  SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  runApp(const NotchDemoApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'notch demo',
      theme: ThemeData.dark(useMaterial3: true),
      home: const DemoPage(),
    );
  }
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  int _emergeKey = 0;
  int _resolveGeneration = 0;
  Notch? _resolved;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Re-resolves on MediaQuery changes (e.g. rotation) for the exact path.
    // The generation guard drops stale results if a newer resolve finishes
    // first.
    final generation = ++_resolveGeneration;
    Notch.resolve(context).then((notch) {
      if (mounted && generation == _resolveGeneration) {
        setState(() => _resolved = notch);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    final notch = _resolved ?? Notch.of(context);
    return Scaffold(
      body: NotchOverlay(
        notch: notch,
        color: Colors.lightBlueAccent,
        child: Stack(
          children: [
            SafeArea(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Camera cutout',
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                    const SizedBox(height: 12),
                    _InfoCard(notch: notch),
                    const Spacer(),
                    Center(
                      child: FilledButton.icon(
                        onPressed: () => setState(() => _emergeKey++),
                        icon: const Icon(Icons.animation),
                        label: const Text('Emerge from cutout'),
                      ),
                    ),
                    const SizedBox(height: 32),
                  ],
                ),
              ),
            ),
            if (_emergeKey > 0)
              NotchEmerge(
                key: ValueKey(_emergeKey),
                child: Center(
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 24,
                      vertical: 16,
                    ),
                    decoration: BoxDecoration(
                      color: Colors.lightBlueAccent,
                      borderRadius: BorderRadius.circular(24),
                    ),
                    child: const Text(
                      'Hello from the cutout 👋',
                      style: TextStyle(
                        color: Colors.black,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

class _InfoCard extends StatelessWidget {
  const _InfoCard({required this.notch});

  final Notch notch;

  @override
  Widget build(BuildContext context) {
    final rect = notch.primaryBounds;
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Type: ${notch.type.name}'),
            if (rect != null)
              Text(
                'Bounds: left=${rect.left.toStringAsFixed(0)}, '
                'top=${rect.top.toStringAsFixed(0)}, '
                'right=${rect.right.toStringAsFixed(0)}, '
                'bottom=${rect.bottom.toStringAsFixed(0)}',
              ),
            Text('Exact path: ${notch.isExactPath}'),
            Text(
              'Safe insets: top=${notch.safeInsets.top.toStringAsFixed(0)}, '
              'left=${notch.safeInsets.left.toStringAsFixed(0)}, '
              'right=${notch.safeInsets.right.toStringAsFixed(0)}, '
              'bottom=${notch.safeInsets.bottom.toStringAsFixed(0)}',
            ),
          ],
        ),
      ),
    );
  }
}
2
likes
160
points
11
downloads

Documentation

API reference

Publisher

verified publishermehuldadlani.dev

Weekly Downloads

Detect the position and shape of the device's camera cutout (notch, hole-punch, Dynamic Island) and use it as the origin of overlays and animations. Android and iOS.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on notch

Packages that implement notch