native_kit
One Dart call invokes the real OS-level UI. native_kit presents genuine
system view controllers, sheets, zero-permission pickers and hardware scanners
on iOS (UIKit / SwiftUI / VisionKit / PencilKit) and Android (Material 3 /
Jetpack / ML Kit / system intents) — no Flutter look-alikes, no Swift or
Kotlin to write.
final choice = await NativeKit.showActionSheet(
context: context, // anchors the iPad popover + syncs your theme
items: const [
NativeMenuItem(id: 'share', title: 'Share', iosSymbol: 'square.and.arrow.up'),
NativeMenuItem(id: 'delete', title: 'Delete', style: NativeActionStyle.destructive),
],
);
Why native_kit
| Drawn by Flutter | Drawn by the OS with native_kit | |
|---|---|---|
| Date picker | showDatePicker / CupertinoDatePicker look-alikes |
UIDatePicker on iOS, MaterialDatePicker on Android |
| Action sheet & menus | Re-created widgets | UIAlertController + UIEditMenuInteraction, Material 3 sheets + ListPopupWindow |
| Color picker | Third-party widgets | UIColorPickerViewController with the system eyedropper |
| OS redesigns (e.g. iOS 26 Liquid Glass) | Wait for widgets to be re-implemented | Picked up from the OS |
| Photos, contacts, documents | Often require permissions | Out-of-process system pickers, no permission prompts |
| Scanners, OCR, subject lift | Camera plugin + ML wiring | VisionKit / Vision and ML Kit in one call |
One dependency covers photo / contact / document pickers, document saving, calendar events, a location picker, share sheet, QuickLook-style preview, in-app browser, printing, translation, dictionary, speech-to-text, biometrics, in-app review, haptics, system panels, document and barcode scanning, OCR, background removal, ink markup and video trimming.
See it run
The same Dart call on iOS (left) and Android (right), recorded from the example app by its UI test suites:
Highlights
- 30 unified components across 6 domains, 32 typed methods.
- Clean Dart models —
DateTime,Color,Rect,Uint8Listand typed result classes. Transport DTOs are never exposed. - Context aware — pass a
BuildContextand the native UI anchors on that widget (iPad-safe popovers) and follows the Flutter theme's brightness and primary color. - No hanging futures — swipe-to-dismiss, back and predictive-back
gestures always resolve the call (with
null,falseor an empty list). - No
MainActivitychanges — Android activity results run through a headless fragment, even with a plainFlutterActivity. - Tested on real system UI — XCUITest and UI Automator suites drive every component on the iOS Simulator and an Android emulator.
Installation
flutter pub add native_kit
import 'package:native_kit/native_kit.dart';
Platform setup
iOS (16.0+)
Set platform :ios, '16.0' in ios/Podfile (Swift Package Manager is
supported too). Add the usage descriptions for the components you use to
ios/Runner/Info.plist:
| Key | Needed by |
|---|---|
NSCameraUsageDescription |
scanDocument, scanCode, extractLiveText (camera) |
NSMicrophoneUsageDescription, NSSpeechRecognitionUsageDescription |
listenSpeechPrompt |
NSFaceIDUsageDescription |
authenticate on Face ID devices |
NSCalendarsUsageDescription |
createCalendarEvent on iOS 16 only (iOS 17+ needs none) |
Missing keys never crash the app: the call throws a NativeKitException
with NativeKitErrorCode.missingUsageDescription.
Android (API 26+)
Set minSdk = 26 in android/app/build.gradle(.kts). That's it: the plugin
ships its own manifest entries (a transparent host activity, a
FileProvider, package-visibility queries and the install-time INTERNET,
VIBRATE and USE_BIOMETRIC permissions). ML Kit models are downloaded by
Google Play services on first use.
Architecture
┌──────────────────────── packages/native_kit ────────────────────────┐
App code ─▶│ NativeKit.showAlert(context: …) → domain models → Pigeon DTOs │
└──────────────────────────────────┬──────────────────────────────────┘
│ NativeKitPlatform (6 Pigeon host APIs)
┌──────────────── native_kit_platform_interface ──────────────────────┐
│ pigeons/native_kit_api.dart → Dart / Swift / Kotlin │
└───────────────┬─────────────────────────────────┬───────────────────┘
▼ ▼
native_kit_ios (Swift, @MainActor) native_kit_android (Kotlin, coroutines)
OverlaysHandler · ValuePickersHandler · same 6 handlers, Material 3 + Jetpack,
ZeroPermPickersHandler · VisionAndMediaHandler NativeKitResultFragment for every
SystemUtilitiesHandler · SecurityAndHardware… ActivityResultContract
Every call carries an optional NativeRect (the calling widget's global
rect) and NativeThemeConfig (brightness + seed color) extracted from the
BuildContext.
Components
Module 1 — Overlays
| Dart method | iOS | Android |
|---|---|---|
showAlert |
UIAlertController(.alert) + addTextField |
MaterialAlertDialogBuilder + TextInputEditText |
showActionSheet |
UIAlertController(.actionSheet) (iPad popover) |
Material 3 BottomSheetDialog list |
showContextMenu |
UIMenu via UIEditMenuInteraction |
ListPopupWindow at the anchor |
showNativeTooltip |
.popover UIViewController with arrow |
Material 3 PopupWindow card with arrow |
showAdaptiveSheet |
UISheetPresentationController detents |
BottomSheetDialog + BottomSheetBehavior |
Module 2 — Value pickers
| Dart method | iOS | Android |
|---|---|---|
pickDateTime / pickDateRange |
UIDatePicker in a .medium() sheet |
MaterialDatePicker / MaterialTimePicker |
pickColor |
UIColorPickerViewController (eyedropper, alpha) |
Material 3 sheet: preview, hex, ARGB / HSL sliders |
pickValueWheel |
multi-column UIPickerView sheet |
NumberPicker row in a BottomSheetDialog |
pickFont |
UIFontPickerViewController |
searchable system typeface sheet |
Module 3 — Zero-permission pickers
| Dart method | iOS | Android |
|---|---|---|
pickMedia |
PHPickerViewController |
PickVisualMedia / PickMultipleVisualMedia |
pickContact |
CNContactPickerViewController |
PickContact / ACTION_PICK + ContentResolver |
pickDocument |
UIDocumentPickerViewController(forOpeningContentTypes:asCopy:) |
OpenDocument / OpenMultipleDocuments |
saveDocument |
UIDocumentPickerViewController(forExporting:asCopy:) |
CreateDocument + stream write |
createCalendarEvent |
EKEventEditViewController (.ics QuickLook on iOS 16) |
ACTION_INSERT on CalendarContract.Events |
pickLocation |
MKMapView + MKLocalSearchCompleter sheet |
map sheet + android.location.Geocoder |
Module 4 — Vision & media
| Dart method | iOS | Android |
|---|---|---|
scanDocument |
VNDocumentCameraViewController → JPEGs + PDFDocument |
ML Kit GmsDocumentScanner |
scanCode |
DataScannerViewController / AVCaptureMetadataOutput |
GmsBarcodeScanner (no camera permission) |
extractLiveText |
VNRecognizeTextRequest / DataScannerViewController(.text()) |
ML Kit Text Recognition + review sheet |
extractImageSubject |
VNGenerateForegroundInstanceMaskRequest (iOS 17+) |
ML Kit Subject Segmentation |
showMarkupSheet |
PKCanvasView + PKToolPicker |
low-latency Canvas full-screen dialog |
trimVideo |
UIVideoEditorController |
trim sheet with MediaExtractor + MediaMuxer |
Module 5 — System utilities
| Dart method | iOS | Android |
|---|---|---|
share |
UIActivityViewController |
Intent.createChooser + FileProvider |
previewFile |
QLPreviewController |
PdfRenderer pager / ACTION_VIEW |
openInAppBrowser |
SFSafariViewController (.pageSheet) |
CustomTabsIntent (Partial Custom Tabs) |
printDocument |
UIPrintInteractionController (+ UIMarkupTextPrintFormatter) |
PrintManager + PrintDocumentAdapter / PrintHelper |
showTranslationSheet |
SwiftUI .translationPresentation (iOS 17.4+) |
ACTION_PROCESS_TEXT / ACTION_TRANSLATE / TTS sheet |
showDictionaryLookUp |
UIReferenceLibraryViewController |
ACTION_DEFINE / Partial Custom Tab search |
listenSpeechPrompt |
SFSpeechRecognizer + AVAudioEngine sheet |
RecognizerIntent.ACTION_RECOGNIZE_SPEECH |
Module 6 — Security & hardware
| Dart method | iOS | Android |
|---|---|---|
authenticate |
LAContext.evaluatePolicy |
BiometricPrompt / KeyguardManager |
requestAppReview |
StoreKit AppStore.requestReview(in:) |
Play ReviewManager (+ store fallback) |
triggerHaptic |
UIImpact/Notification/SelectionFeedbackGenerator, CHHapticEngine |
HapticFeedbackConstants, VibrationEffect |
showSystemPanel |
AVRoutePickerView, NEHotspotConfigurationManager, MPVolumeView, RPScreenRecorder |
MediaRouter2 switcher, Settings.Panel.*, MediaProjectionManager |
Usage
// Anchored, themed action sheet (popover on iPad).
final choice = await NativeKit.showActionSheet(
context: buttonContext,
items: const [
NativeMenuItem(id: 'share', title: 'Share', iosSymbol: 'square.and.arrow.up'),
NativeMenuItem(id: 'delete', title: 'Delete', style: NativeActionStyle.destructive),
],
);
// Zero-permission photo picker.
final List<PickedMedia> photos = await NativeKit.pickMedia(
type: NativeMediaType.images,
selectionLimit: 5,
);
// Native color picker returns a Flutter Color.
final Color? color = await NativeKit.pickColor(context: context);
// Document scanner returns page JPEGs and a combined PDF.
final DocumentScanResult? scan = await NativeKit.scanDocument();
// The instance API is the same surface, handy for dependency injection.
final NativeKitClient kit = NativeKit.instance;
await kit.triggerHaptic(HapticType.success);
Error handling
Cancellation is never an error. Real failures throw NativeKitException
with a NativeKitErrorCode: noUiContext, unavailable (e.g. VisionKit on
the simulator), invalidArgument, missingUsageDescription,
permissionDenied, ioError or nativeError.
try {
await NativeKit.scanCode();
} on NativeKitException catch (e) {
if (e.code == NativeKitErrorCode.unavailable) showFallbackUi();
}
Platform notes
- Files returned to Dart (media, documents, scans, cutouts, trimmed videos) are copies in the app's temporary / cache directory. Move what you keep.
pickLocationon Android renders OpenStreetMap tiles by default. PassandroidTileUrlTemplateto use your own tile server in production.showSystemPanel(SystemPanel.screenCapture)records in-app on iOS (call it again to stop and receive the movie) and returns theMediaProjectionconsent result on Android.showSystemPanel(SystemPanel.nfc)is Android only.
Packages
| Package | Purpose |
|---|---|
native_kit |
App-facing API (this package) |
native_kit_platform_interface |
Pigeon contract and NativeKitPlatform |
native_kit_ios |
iOS implementation |
native_kit_android |
Android implementation |
License
MIT — see LICENSE.
Missing a native component? Request it — new components are added to the shared Pigeon bridge for both platforms.
Libraries
- native_kit
- One Dart call invokes the real OS-native UI on iOS and Android.