Architecture overview
Principle#
The Native IR is the single source of truth. Parsers produce IR; generators consume IR. No generator reads SDK files or documentation directly, and documentation is never a source of signatures.
Platform SDK artifacts Official metadata Official annotations
(android.jar class files) (api-versions.xml availability) (class-file + annotations.zip)
└───────────────────────────┬───────────────────────────────┘
↓
Platform parser (native_api_android)
↓
Canonical Native IR (native_api_ir)
↓
Validation · normalization · dependency graph · type mapping (native_api_generator)
┌──────────────────┴───────────────────┐
Flutter target React Native target (planned)
native_api_flutter_android native_api_react_native_android
Dart + package:jni (JNI) TS + C++/JSI + JNI
└──────────────────┬───────────────────┘
Native platform
Packages#
| Package | Responsibility | Depends on |
|---|---|---|
native_api_ir |
IR data model, stable symbol IDs, canonical JSON, validation, diagnostic codes | — |
native_api_core |
project info/versions, configuration, structured logging, path guard, license audit, dependency graph, IR diff | ir |
native_api_android |
Android SDK discovery, zip + JVM class-file + generic-signature parsers, api-versions/annotations enrichment, IR construction | ir, core |
native_api_generator |
target-independent generation services: type mapping, identifier escaping, overload naming, deterministic file writer, binding map | ir, core |
native_api_flutter_android |
emits Dart extension types over package:jni |
ir, core, generator |
native_api_cli (pub name native_api_bindgen) |
command line: wiring only, no business logic | all of the above |
runtimes/dart/native_api_runtime |
small runtime used by generated code: availability guard, error model, handle helpers | package:jni |
native_api_ios, native_api_flutter_ios, native_api_react_native_* |
placeholders; CLI reports not yet implemented diagnostics |
— |
Dependencies point downward only (enforced by pubspec). Parsers do not know generators exist; generators do not know where IR came from.
Data flow for generate flutter --entry android.content.Intent#
AndroidSdkLocatorresolves the SDK (ANDROID_HOME → ANDROID_SDK_ROOT → OS default) and platform (platform.android.sdkor highest stable).AndroidJarReaderlists class entries;ClassFileParserparses only requested classes on demand (lazy, bounded).ApiVersionsIndexandAnnotationsIndexattach availability, nullability, threading, permissions, IntDef.DependencyGraphcomputes the closure from the entry symbol up to the configured depth. Types outside the closure are opaque: they map toJObjectand get a diagnostic note, they are never silently pulled in.IrValidatorchecks invariants (unique IDs, resolvable references, no hidden API marked public).DartJniEmittergenerates one Dart library per Java package plus an umbrella library;GeneratedFileWriterwrites through the path guard and records abinding_map.json(generated symbol → native symbol).coverage.jsonandir.jsonare written next to the output forcoverage,why-generated,why-skipped.
Generated-code shape (Flutter Android)#
Each Java class becomes a Dart extension type over JObject. Extension types are zero-cost wrappers and are tree-shakable: there is no registry, no reflection and no startup registration, so unused classes and members are removed by the Dart AOT compiler. Method IDs are static final lazily-initialised fields, resolved on first use only.
See also: ir.md, type-mapping.md, lifecycle.md.
Source:
docs/architecture/overview.md