FlutterFever Studio
Back to packages

Safaeh Flutter package guide

Adaptive UI components – sheets, dialogs, QR scanner, page index and side navigation for Flutter.

Install command
Copy and run in your Flutter project
flutter pub add safaeh

When to use Safaeh

Safaeh is a purpose‑built Flutter package that brings a collection of adaptive UI primitives to your project, letting you create bottom sheets, modal dialogs, QR‑code scanners, page index indicators and side navigation panels with a single, consistent API. The library follows Material‑3 guidelines while automatically adjusting its layout for phones, tablets, web browsers and desktop windows. This means the same widget can render as a full‑screen modal on a small handset, a centered dialog on a desktop, or a persistent side panel on a large screen without any extra code from you.

When you need a quick way to present contextual actions or supplemental information, Safaeh’s adaptive sheets replace the boilerplate of building a `showModalBottomSheet` that behaves differently on each platform. The sheet automatically switches to a drawer‑style panel on wide screens, preserving the user’s mental model of navigation. The same component can be used for onboarding steps, filter panels, or confirmation prompts, and it supports custom theming, drag‑to‑dismiss gestures, and safe‑area handling out of the box.

Dialogs are another core focus. Safaeh’s `AdaptiveDialog` respects the platform’s modal conventions – it appears as a centered card on desktop, a bottom‑sheet‑styled dialog on mobile, and a full‑screen overlay on web when space is constrained. The widget exposes slots for title, content, and actions, and it integrates with the Flutter `Navigator` to return typed results, making it ideal for form submissions, error handling, or simple yes/no confirmations. Because the dialog is adaptive, you no longer need to write separate widget trees for each breakpoint.

For apps that rely on camera input, Safaeh includes a lightweight QR‑code scanner that works across mobile, web and desktop. The scanner leverages the `camera` plugin on native platforms and the `html` `MediaDevices` API on the web, abstracting away platform differences. It provides a ready‑to‑use overlay with a cut‑out rectangle, automatic focus handling, and a stream of decoded strings, allowing you to implement ticket validation, product lookup, or contact sharing with just a few lines of code.

Navigation in larger layouts often requires a side navigation panel that can collapse, expand, or hide based on screen width. Safaeh’s `AdaptiveSideNav` offers a configurable list of destinations, icons, and badge counters, and it automatically transitions between a permanent drawer, a collapsible rail, or a modal drawer depending on the current breakpoint. This helps you maintain a consistent navigation experience across devices without duplicating logic.

Integrating Safaeh into a Flutter architecture is straightforward. Because the package does not enforce any state‑management solution, you can use Provider, Riverpod, Bloc, or even plain `setState`. The UI components emit callbacks (e.g., `onDismissed`, `onScanned`) that can be wired into your chosen architecture. In a clean‑architecture setup, the UI layer would call a use‑case or repository that handles business logic, while the adaptive components remain pure presentation widgets. The package also respects the app’s `ThemeData`, automatically applying light/dark colors and elevation values, which reduces the need for custom styling.

Getting started is simple: add the dependency, import the library, and drop an `AdaptiveSheet` or `AdaptiveDialog` into your widget tree. The documentation includes a step‑by‑step guide for handling camera permissions on Android, iOS and web, as well as best‑practice tips for performance – such as reusing the scanner controller and limiting the frame rate on low‑end devices. For production use, consider testing the QR scanner on all target platforms, handling permission denial gracefully, and providing fallback UI for browsers that block camera access. Overall, Safaeh offers a balanced mix of flexibility and opinionated defaults that accelerate UI development while keeping the codebase maintainable.

responsive bottom sheets for filters or actions
modal dialogs that adapt to screen size
integrated QR‑code scanning without native code
side navigation that collapses on small devices
page index indicators for onboarding flows

Pros

  • lightweight
  • adaptive design out of the box
  • single API for multiple UI patterns
  • no native code required for core features
  • well‑documented with examples

Watch outs

  • limited custom animation hooks
  • camera permission handling required on each platform
  • web camera may need HTTPS and user consent

Setup notes

Add the package to your pubspec.yaml with the command: ``` flutter pub add safaeh ``` Then run `flutter pub get` and import the library where needed: ```dart import 'package:safaeh/safaeh.dart'; ```

Safaeh requires Flutter 3.3 or newer. It supports Android, iOS, web, macOS, Linux and Windows. Camera functionality needs the appropriate permissions declared in AndroidManifest.xml, Info.plist and the web's HTTPS context. The package does not depend on any native SDK beyond the standard `camera` plugin, so it works with the default Flutter toolchain.

```dart
import 'package:flutter/material.dart';
import 'package:safaeh/safaeh.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Safaeh Demo',
      theme: ThemeData.useMaterial3(true),
      home: const HomePage(),
    );
  }
}

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

  void _showSheet(BuildContext ctx) {
    AdaptiveSheet.show(
      context: ctx,
      title: const Text('Pick an option'),
      children: [
        ListTile(title: const Text('Option A'), onTap: () => Navigator.pop(ctx, 'A')),
        ListTile(title: const Text('Option B'), onTap: () => Navigator.pop(ctx, 'B')),
      ],
    );
  }

  void _scanQr(BuildContext ctx) async {
    final result = await AdaptiveQrScanner.scan(context: ctx);
    if (result != null) {
      ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(content: Text('Scanned: $result')));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Safaeh Demo')),
      drawer: const AdaptiveSideNav(
        destinations: [
          AdaptiveNavDestination(icon: Icons.home, label: 'Home'),
          AdaptiveNavDestination(icon: Icons.settings, label: 'Settings'),
        ],
      ),
      body: Center(
        child: Column(mainAxisSize: MainAxisSize.min, children: [
          ElevatedButton(onPressed: () => _showSheet(context), child: const Text('Show Sheet')),
          const SizedBox(height: 16),
          ElevatedButton(onPressed: () => _scanQr(context), child: const Text('Scan QR')),
        ]),
      ),
    );
  }
}
```

Official package resources