Examples

Examples

The example apps do real work through generated bindings, with no plugin and no hand-written native code: a live device dashboard, text-to-speech, the share sheet, the clipboard, a persistent note, haptics and opening URLs. The same tasks run in Flutter and React Native on Android and iOS, and every task is covered by an on-device test.

TaskAndroid APIsiOS APIs
Device dashboardbattery broadcast + BatteryManager, ActivityManager.MemoryInfo, StatFs, ConnectivityManagerUIDevice, NSProcessInfo, NSFileManager
Speak textTextToSpeech + an OnInitListener written in Dart or JSAVSpeechSynthesizer
Share textACTION_SEND + Intent.createChooserUIActivityViewController
Copy and pasteClipboardManager + ClipDataUIPasteboard
Persistent noteSharedPreferencesNSUserDefaults
FeedbackVibrator + VibrationEffect (API 26+, runtime-guarded)UIImpactFeedbackGenerator, UINotificationFeedbackGenerator
Location settingsSettings.ACTION_LOCATION_SOURCE_SETTINGS; the app's permission page via ACTION_APPLICATION_DETAILS_SETTINGS + package: URIthe app's page in Settings (UIApplicationOpenSettingsURLString), where its location permission is shown
Open a URLACTION_VIEW + startActivityopenURL:options:completionHandler: (block → Dart / JS)

Apps: examples/flutter/android_slice, examples/flutter/ios_slice and examples/react-native/slice (Android and iOS).

Device dashboard

Flutter on Android: battery from the sticky broadcast, memory, free storage and the active network.

final battery = ctx.registerReceiver(
    null, IntentFilter.new$String(Intent.ACTION_BATTERY_CHANGED.toJString()))!;
final level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL.toJString(), -1);

final am = ctx.getSystemService(Context.ACTIVITY_SERVICE.toJString())!.as(ActivityManager.type);
final mem = ActivityManager_MemoryInfo();
am.getMemoryInfo(mem);                                  // mem.availMem, mem.totalMem

final fs = StatFs(Environment.getDataDirectory()!.getAbsolutePath());
print('${fs.getAvailableBytes()} of ${fs.getTotalBytes()} bytes free');

Flutter on iOS:

final device = ios.UIDevice.currentDevice..batteryMonitoringEnabled = true;
final info = ios.NSProcessInfo.processInfo;
print('${device.batteryLevel} · ${info.thermalState} · ${info.physicalMemory} bytes');

Text-to-speech with a callback

Android reports that the engine is ready through TextToSpeech.OnInitListener, a Java interface implemented here in JavaScript (React Native):

const engine = TextToSpeech.new(applicationContext(), TextToSpeech_OnInitListener.implement(
  { onInit: status => {
      if (status === TextToSpeech.SUCCESS)
        engine.speak$CharSequence$int$Bundle$String(text, TextToSpeech.QUEUE_FLUSH, null, 'id');
  } },
  { async: ['onInit'] },
));

iOS (React Native):

const utterance = AVSpeechUtterance.speechUtteranceWithString(text);
utterance.voice = AVSpeechSynthesisVoice.voiceWithLanguage(AVSpeechSynthesisVoice.currentLanguageCode());
synth.speakUtterance(utterance);

Share sheet

Flutter on iOS: find the key window through the connected scenes, then present UIActivityViewController.

final sheet = ios.UIActivityViewController.alloc()
    .initWithActivityItems(objc.NSArray.of([text.toNSString()]));
sheet.popoverPresentationController?.sourceView = root.view;   // iPad
await root.presentViewControllerAsync(sheet, animated: true);

React Native on Android:

const send = Intent.new$String(Intent.ACTION_SEND)
  .setType('text/plain')
  .putExtra$String$String(Intent.EXTRA_TEXT, text);
currentActivity()!.startActivity(Intent.createChooser(send, 'Share via')!);

Clipboard and a persistent note

// Flutter on Android
clipboard.setPrimaryClip(ClipData.newPlainText('label'.toJString(), text.toJString())!);
prefs.edit()!.putString('note'.toJString(), note.toJString())!.apply();

// React Native on iOS: setObject:forKey: takes `id`, so wrap the string
UIPasteboard.generalPasteboard.string_ = text;
NSUserDefaults.standardUserDefaults.setObject(nsString(note), 'showcase.note');

Location settings

Flutter on Android: open the system Location page, or this app's own settings page, where location permission is granted.

activity.startActivity(Intent.new$String(Settings.ACTION_LOCATION_SOURCE_SETTINGS.toJString()));

activity.startActivity(Intent.new$String$Uri(
  Settings.ACTION_APPLICATION_DETAILS_SETTINGS.toJString(),
  Uri.parse('package:${ctx.getPackageName()!.toDartString()}'.toJString()),
));

APIs newer than your minimum SDK

VibrationEffect is API 26+, newer than the example's minApi: 24. The generated call checks the device and throws instead of crashing:

try {
  vibrator.vibrate$VibrationEffect(VibrationEffect.createOneShot(60, VibrationEffect.DEFAULT_AMPLITUDE));
} on NativeApiUnavailableException {
  vibrator.vibrate(60);                                  // legacy API on older devices
}

More binding features

// Kotlin: suspend -> Future / Promise, Flow -> Stream
final greeting = await greeter.greetLater(20);
await for (final i in greeter.countTo(3)) print(i);

// Objective-C: NSError ** -> exception, completion handler -> Future
try {
  fileManager.contentsOfDirectoryAtPath('/missing'.toNSString());
} on ios.NativeObjCError catch (e) {
  print(e.domain);                                       // NSCocoaErrorDomain
}

// Blocks and protocols written in JavaScript
fileManager.delegate = NSFileManagerDelegate.implement({
  fileManager$shouldRemoveItemAtPath: () => false,
});

// A native view in the widget tree / component tree
NativeView.ios(view: ios.UILabel.new$()..text = 'Hello'.toNSString());
<NativeView view={label} style={{ width: 200, height: 48 }} />