native_kit 0.1.1
native_kit: ^0.1.1 copied to clipboard
One Dart call invokes the real OS-native UI on iOS and Android - system sheets, alerts, zero-permission pickers, scanners, share, print and haptics.
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.