System Architecture & Platform Bridge
Neon's performance and developer experience stem from its decoupled architecture. Rather than running an embedded C++ canvas engine or interpreting JavaScript via an asynchronous bridge, Neon runs a Dart UI server that instructs native Android and iOS platform shells via an optimized JSON protocol.
The Platform Bridge Pattern
The core interaction between the Dart framework and native operating systems follows a high-throughput, low-latency client-server model:
┌────────────────────────┐
│ User Action │ (Tap, Swipe, Input, Toggle)
└───────────┬────────────┘
│
▼
┌────────────────────────┐
│ Native Shell (OS) │
│ Android: Kotlin View │
│ iOS: UIKit UIView │
└───────────┬────────────┘
│
│ HTTP POST /action
│ { "id": "btn_submit", "clientRevision": 1 }
▼
┌────────────────────────┐
│ Dart Backend Server │ (Runs on localhost:8080)
│ 1. Routes to Widget │
│ 2. Executes Callback │
│ 3. Flushes Microtasks │
│ 4. Rebuilds Tree │
│ 5. NeonWidgetTreeDiff │ (Computes delta from baseRevision)
└───────────┬────────────┘
│
│ HTTP Response (200 OK)
│ { "type": "patch", "revision": 2, "operations": [...] }
│ (or "type": "full" if revision mismatch / recovery)
▼
┌────────────────────────┐
│ Native Shell (OS) │
│ Applies patch in-place│ (Preserves native view state & focus)
│ or recovers full tree │
└────────────────────────┘
1. The Dart Engine Backend
The Dart backend serves as the brain of the application:
NeonApp.run(): Initializes the application loop and spins up an HTTP server (default port8080).GET /api/tree: Returns the initial full serialized representation of the activeNeonWidgetTreeencapsulated withtype: "full",version: 1, andrevision: 1.POST /action: Receives user interactions dispatched from the native shell:{ "id": "counter-btn-add", "clientRevision": 1, "patchVersion": 1, "value": null }- Action Dispatcher: Looks up the target widget by its unique ID in the element tree, invokes its registered event listener (e.g.
onPressed,onChanged), and triggerssetState(). - Microtask Queue Flush: The server waits for the microtask queue to drain (
await Future.delayed(Duration.zero)) before rebuilding the tree. - Incremental Patching: Using
NeonWidgetTreeDiff, the engine calculates atomic operations (update,replace,remove,insert) relative to the client'sclientRevision. If revisions match, it transmits a lightweighttype: "patch"response. If a revision mismatch occurs or recovery is requested, it seamlessly falls back to a completetype: "full"response. For detailed information, see Widget Tree Diffing.
2. Native Shell Implementations
Android Native Shell (MainActivity.kt)
- Built using modern Kotlin and Gradle targeting Android SDK 21–34.
- Uses native
java.net.URLand coroutines to establish high-speed local HTTP requests. - Maps JSON elements to real Android components:
Container➔FrameLayout/LinearLayoutFilledButton/ElevatedButton➔com.google.android.material.button.MaterialButtonTextField➔com.google.android.material.textfield.TextInputEditTextSwitch➔com.google.android.material.materialswitch.MaterialSwitchListView➔androidx.recyclerview.widget.RecyclerView
iOS Native Shell (ViewController.swift)
- Built using Swift and UIKit (targeting iOS 15.0+).
- Uses standard
URLSessionwith background streaming. - Maps JSON elements to real Cocoa Touch views:
Container➔UIViewwithCALayerstyling (corner radius, border, drop shadows)Button➔UIButton(with iOS 15+UIButton.Configuration)TextField➔UITextFieldSwitch➔UISwitchListView➔UITableView/UICollectionView
3. Critical Framework Design Decisions
Interactive Widget Keys
Because the native UI layer is decoupled from Dart memory pointers, every interactive widget requires a stable String key:
// Recommended: Explicit string keys for interactive elements
FilledButton(
key: 'login-submit-button',
onPressed: _handleLogin,
child: Text('Log In'),
)
Without a key, the native shell cannot bind tap or input gestures back to the corresponding Dart event listener.
double.infinity Clamping
The standard JSON specification does not support double.infinity or double.nan. When developers specify unbounded layouts (e.g., width: double.infinity), the Neon rendering pipeline automatically clamps infinity to a sentinel value (-1.0 or MATCH_PARENT) before serialization so that the native shells can apply full-width/full-height layout constraints.
4. Comparison: Neon vs Flutter vs React Native
| Dimension | Neon Framework | Flutter | React Native |
| :--- | :--- | :--- | :--- |
| Render Engine | 100% Native OS Views (UIKit & Material) | Skia / Impeller custom canvas | Native OS Views |
| Logic Language | Dart 3 | Dart 3 | JavaScript / TypeScript |
| Engine Overhead | ~0 MB (no custom graphics engine) | ~15MB–25MB engine runtime | ~8MB–12MB JS runtime (Hermes) |
| Platform Look & Feel | Flawless Native (Platform handles text selection, accessibility, haptics) | Canvas-drawn approximation | Native |
| Hot Reload | 3-Tier Sub-100ms | State-based Hot Reload | Fast Refresh |
| Access to Platform APIs | Direct via Native Shell HTTP bridge | Platform Channels / FFI | JSI / TurboModules |
Next Steps
- Explore Reactive State Management with
NeonSignal. - Learn how to use the Flutter-to-Neon Transpiler to port existing codebases.
- Review Android and iOS Production Deployment.