Skip to content

Commit e97ee2f

Browse files
committed
Make cnativeapi and nativeapi plain Dart; move Flutter code to nativeapi_flutter
cnativeapi is no longer a Flutter FFI plugin. A build hook (hook/build.dart, native_toolchain_c) compiles core with the target's toolchain, replacing the per-platform CMake, CocoaPods and SwiftPM projects and the generated Apple source wrappers. ffigen (now 22) emits top-level @Native functions bound to that code asset, so the CNativeApiBindings class and cnativeApiBindings are gone. codegen.py is folded into ./codegen as `./codegen ffigen`; the release workflow only vendors core into cxx_impl/. nativeapi drops its Flutter dependency. Point, Size, Rectangle and Color are generated as nativeapi's own value types instead of dart:ui's Offset, Size, Rect and Color, and every generated value type gets ==, hashCode and toString over its data fields (callbacks left out). nativeapi_flutter now holds the Flutter side: the widgets, ImageAsset, windowing.dart, the widget tests and the example app, plus conversions to and from dart:ui (toOffset/toSize/toRect/toColor, toNative). Its re-export of nativeapi hides Brightness, Color, Display, Image and Size, which would silently shadow dart:ui, and ModifierKey and ShortcutManager, which are ambiguous with Flutter's. The flutter_* examples depend on nativeapi_flutter and convert at the nativeapi boundary; those that name a hidden type import nativeapi with a prefix.
1 parent 7e431b4 commit e97ee2f

580 files changed

Lines changed: 5496 additions & 14678 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/core-api-change/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ core pointer plus everything regenerated under `bindings/`. The bindings build a
145145
**A binding has unrelated work — manual path.** Same steps, staged narrowly:
146146

147147
1. Commit core yourself (`git -C core add <paths> && git -C core commit -m ...`).
148-
2. `./codegen`; rust → rerun bindgen (command in `tools/codegen/README.md`); flutter →
149-
`python3 codegen.py` in `bindings/dart/cnativeapi`.
148+
2. `./codegen`; rust → rerun bindgen (command in `tools/codegen/README.md`); dart →
149+
`./codegen ffigen`.
150150
3. Workspace: `git add core` plus only the generated paths under `bindings/`; commit as
151151
`Sync with core <sha9>`.
152152

‎.github/workflows/dart-ci.yml‎

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,9 @@ jobs:
133133
distribution: 'temurin'
134134
java-version: '17'
135135

136-
- name: Build cnativeapi example Android
137-
run: |
138-
cd bindings/dart/cnativeapi/example
139-
flutter build apk --release
140-
141136
- name: Build nativeapi example Android
142137
run: |
143-
cd bindings/dart/nativeapi/example
138+
cd bindings/dart/nativeapi_flutter/example
144139
flutter build apk --release
145140
146141
# iOS build
@@ -172,14 +167,9 @@ jobs:
172167
- name: Bootstrap melos
173168
run: dart pub global run melos bootstrap
174169

175-
- name: Build cnativeapi example iOS
176-
run: |
177-
cd bindings/dart/cnativeapi/example
178-
flutter build ios --release --no-codesign
179-
180170
- name: Build nativeapi example iOS
181171
run: |
182-
cd bindings/dart/nativeapi/example
172+
cd bindings/dart/nativeapi_flutter/example
183173
flutter build ios --release --no-codesign
184174
185175
# Linux build
@@ -225,14 +215,14 @@ jobs:
225215
libx11-dev \
226216
libxi-dev
227217
228-
- name: Build cnativeapi example Linux
229-
run: |
230-
cd bindings/dart/cnativeapi/example
231-
flutter build linux --release
218+
# cnativeapi is a plain Dart package: its build hook compiles core.
219+
- name: Run cnativeapi example Linux
220+
working-directory: bindings/dart/cnativeapi
221+
run: dart run example/cnativeapi_example.dart
232222

233223
- name: Build nativeapi example Linux
234224
run: |
235-
cd bindings/dart/nativeapi/example
225+
cd bindings/dart/nativeapi_flutter/example
236226
flutter build linux --release
237227
238228
# macOS build
@@ -264,14 +254,14 @@ jobs:
264254
- name: Bootstrap melos
265255
run: dart pub global run melos bootstrap
266256

267-
- name: Build cnativeapi example macOS
268-
run: |
269-
cd bindings/dart/cnativeapi/example
270-
flutter build macos --release
257+
# cnativeapi is a plain Dart package: its build hook compiles core.
258+
- name: Run cnativeapi example macOS
259+
working-directory: bindings/dart/cnativeapi
260+
run: dart run example/cnativeapi_example.dart
271261

272262
- name: Build nativeapi example macOS
273263
run: |
274-
cd bindings/dart/nativeapi/example
264+
cd bindings/dart/nativeapi_flutter/example
275265
flutter build macos --release
276266
277267
# Windows build
@@ -304,12 +294,12 @@ jobs:
304294
- name: Bootstrap melos
305295
run: dart pub global run melos bootstrap
306296

307-
- name: Build cnativeapi example Windows
308-
run: |
309-
cd bindings/dart/cnativeapi/example
310-
flutter build windows --release
297+
# cnativeapi is a plain Dart package: its build hook compiles core.
298+
- name: Run cnativeapi example Windows
299+
working-directory: bindings/dart/cnativeapi
300+
run: dart run example/cnativeapi_example.dart
311301

312302
- name: Build nativeapi example Windows
313303
run: |
314-
cd bindings/dart/nativeapi/example
304+
cd bindings/dart/nativeapi_flutter/example
315305
flutter build windows --release

‎.github/workflows/dart-release.yml‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,13 @@ jobs:
2929
channel: stable
3030
version: 3.47.5
3131

32-
# The cnativeapi package must carry core's sources: copy them into
33-
# cxx_impl/ (untracked; the CMake build prefers it when present) and point
34-
# the Apple source wrappers there. The rewritten wrappers go into a local
35-
# commit that is never pushed, because `dart pub publish` warns about
36-
# modified checked-in files.
32+
# The cnativeapi package must carry core's sources: its build hook
33+
# compiles cxx_impl/ when present (untracked, so the checkout stays clean
34+
# and `dart pub publish` still ships it).
3735
- name: Vendor core into cnativeapi
3836
run: |
3937
mkdir bindings/dart/cnativeapi/cxx_impl
4038
git -C core archive HEAD | tar -x -C bindings/dart/cnativeapi/cxx_impl
41-
python3 bindings/dart/cnativeapi/codegen.py --core-dir bindings/dart/cnativeapi/cxx_impl --sources-only
42-
git -c user.name=release -c user.email=release@localhost commit -qam "Point cnativeapi at its vendored core (not pushed)"
4339
4440
# Publish cnativeapi
4541
- name: Install dependencies (cnativeapi)

‎AGENTS.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ codegen # Python entry point orchestrating the generators
2424

2525
- `core` — the C++ core library (repo: `nativeapi-core`). The source of truth for the native API surface (windows, tray icons, menus, displays, keyboard, dialogs, storage, etc.) with per-platform implementations (macOS/Windows/Linux).
2626
- `tools/codegen` — three crates: `shared` (libclang parser, IR, naming), `capi` (C ABI + umbrella header), `bindings` (Rust/Dart/C# generators, consuming the IR JSON emitted by `capi`). Only `capi` depends on libclang. See tools/codegen/README.md.
27-
- `bindings/*` — language bindings wrapping the core library. All live in this repo and build against the `core/` submodule directly (Rust `build.rs`, the Flutter plugin's CMake and generated Apple source wrappers, the C# native CMake). Only a published package carries its own copy of core, in `cxx_impl/`, which the release workflows vendor and never commit. The Rust binding layers `nativeapi` (safe API) over `cnativeapi` (FFI).
27+
- `bindings/*` — language bindings wrapping the core library. All live in this repo and build against the `core/` submodule directly (Rust `build.rs`, the Dart `cnativeapi` package's build hook, the C# native CMake). Only a published package carries its own copy of core, in `cxx_impl/`, which the release workflows vendor and never commit. The Rust binding layers `nativeapi` (safe API) over `cnativeapi` (FFI).
2828

2929
## Design specs
3030

@@ -63,12 +63,12 @@ A core change ripples to every binding. After editing headers in `core`, run:
6363
./codegen sync -m "<core commit message>"
6464
```
6565

66-
It regenerates everything, reruns `bindgen` (Rust raw FFI) and the Dart binding's `codegen.py` (umbrella headers + ffigen), then commits core and this repo (`Sync with core <sha>`: the core pointer plus everything regenerated under `bindings/`). Add `--push` to publish in dangling-safe order (core → workspace).
66+
It regenerates everything, reruns `bindgen` (Rust raw FFI) and `ffigen` (Dart raw FFI), then commits core and this repo (`Sync with core <sha>`: the core pointer plus everything regenerated under `bindings/`). Add `--push` to publish in dangling-safe order (core → workspace).
6767

6868
Manual follow-ups sync cannot do (details in tools/codegen/README.md):
6969

7070
- New handle types need an `IdTypeTag<T>` entry in `core/src/foundation/id_allocator.h` (append only; a miss is a compile error, not silent).
71-
- Hand-written files in the bindings (exports, re-exports, changelogs, examples) are never touched by the generators. Rust's `pub mod` list is generated (`modules.rs`); Flutter's `lib/nativeapi.dart` exports are not.
71+
- Hand-written files in the bindings (exports, re-exports, changelogs, examples) are never touched by the generators. Rust's `pub mod` list is generated (`modules.rs`), and so is Dart's (`nativeapi/lib/src/generated.dart`); `nativeapi_flutter`'s exports are not.
7272

7373
The `core-api-change` skill walks the whole flow, including what to check before `sync` commits.
7474

@@ -91,10 +91,10 @@ scenarios for *this project's* examples live in [tools/gui/](tools/gui/README.md
9191
## Conventions
9292

9393
- `core` tracks `branch = main`. Use `make sync` to fast-forward it; `make status` to see dirty state everywhere; `make bump` to stage its pointer.
94-
- The leanflutter packages built on nativeapi (`tray_manager`, `window_manager`, `launch_at_startup`, …) live in their own repos under github.com/leanflutter and depend on the published `nativeapi`; they are not part of this repo. To try one against local changes, point a `dependency_overrides` entry in that package at `bindings/dart/nativeapi` (and `cnativeapi`) and never commit the override.
94+
- The leanflutter packages built on nativeapi (`tray_manager`, `window_manager`, `launch_at_startup`, …) live in their own repos under github.com/leanflutter and depend on the published `nativeapi`; they are not part of this repo. To try one against local changes, point a `dependency_overrides` entry in that package at `bindings/dart/nativeapi_flutter` (and `nativeapi`, `cnativeapi`) and never commit the override.
9595
- Commit workspace submodule pointer updates only when the combination is compatible (a known-good snapshot).
9696
- Examples live in `examples/<binding>_<name>_example` (`flutter_`, `rust_`, `csharp_`), not inside the bindings; a new Flutter or Rust example must also be listed in the root `pubspec.yaml` / `Cargo.toml`, a C# one in `bindings/csharp/NativeAPI.slnx`. Only the pub.dev package examples (`bindings/dart/*/example`) stay inside their package.
9797
- CI is one workflow per binding (`dart-ci.yml`, `rust-ci.yml`, `csharp-ci.yml`), each running only for changes under its `bindings/<lang>/` and its examples (`examples/flutter_*` for Dart, `examples/rust_*`, `examples/csharp_*`). Release tags are per binding: `v*` publishes the Dart packages (`dart-release.yml`), `rust-v*` publishes the crates (`rust-release.yml`); never push a bare `v*` tag for anything but the Dart packages.
98-
- The Dart packages `cnativeapi`, `nativeapi` and `nativeapi_flutter` share one version: a release bumps all three pubspecs and CHANGELOGs, because pub.dev's automated publishing matches the tag `v<version>` against each package's own version. `nativeapi_flutter` is the Flutter-facing package; for now it only re-exports `package:nativeapi`.
98+
- The Dart packages `cnativeapi`, `nativeapi` and `nativeapi_flutter` share one version: a release bumps all three pubspecs and CHANGELOGs, because pub.dev's automated publishing matches the tag `v<version>` against each package's own version. `cnativeapi` and `nativeapi` are plain Dart (no Flutter dependency; a build hook compiles core); `nativeapi_flutter` is the Flutter-facing package, holding the widgets, the `dart:ui` conversions and `windowing.dart`, and re-exporting `nativeapi` minus the names that clash with Flutter's.
9999
- Never commit in a submodule while on a detached HEAD — check out `main` first (`./codegen sync` enforces this).
100100
- Do not add Co-Authored-By trailers to commits.

‎bindings/dart/README-ZH.md‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,26 @@
1414

1515
| 包 | 说明 |
1616
| --- | --- |
17-
| [`nativeapi`](nativeapi) | API 本身:窗口、托盘图标、菜单、显示器、键盘、对话框、存储等。 |
18-
| [`cnativeapi`](cnativeapi) | 面向 core C ABI 的原始 FFI 绑定和原生构建,供 `nativeapi` 使用。 |
19-
| [`nativeapi_flutter`](nativeapi_flutter) | 面向 Flutter 的包,目前直接重新导出 `nativeapi`。 |
17+
| [`nativeapi`](nativeapi) | API 本身:窗口、托盘图标、菜单、显示器、键盘、对话框、存储等。纯 Dart,不依赖 Flutter。 |
18+
| [`cnativeapi`](cnativeapi) | 面向 core C ABI 的原始 FFI 绑定,由 build hook 编译 core,供 `nativeapi` 使用。 |
19+
| [`nativeapi_flutter`](nativeapi_flutter) | 给 Flutter 应用用:重新导出 `nativeapi`,并提供 widget、与 `dart:ui` 类型的互转和多窗口桥接。 |
2020

2121
## 安装
2222

23+
Flutter 应用:
24+
2325
```bash
24-
flutter pub add nativeapi
26+
flutter pub add nativeapi_flutter
2527
```
2628

29+
Dart 应用(命令行或其他 Dart 宿主):
30+
31+
```bash
32+
dart pub add nativeapi
33+
```
34+
35+
`nativeapi` 有自己的 `Point`、`Size`、`Rectangle`、`Color` 类型。`nativeapi_flutter` 提供它们与 `dart:ui` 类型的互转(`window.bounds.toRect()`、`Offset(10, 20).toNative()`),并且不重新导出与 Flutter 重名的 nativeapi 名字(`Brightness`、`Color`、`Display`、`Image`、`ModifierKey`、`ShortcutManager`、`Size`);需要写出这些类型时,给 `package:nativeapi/nativeapi.dart` 加 import 前缀。
36+
2737
## 快速开始
2838

2939
```dart
@@ -36,7 +46,7 @@ for (final display in DisplayManager.instance.getAll()) {
3646

3747
### 自定义窗口标题栏
3848

39-
用 `DragToMoveArea` 包裹自定义标题栏即可拖动窗口(双击最大化/还原),用 `DragToResizeArea` 包裹窗口内容即可从边缘和四角调整大小:
49+
引入 `package:nativeapi_flutter/nativeapi_flutter.dart` 后,用 `DragToMoveArea` 包裹自定义标题栏即可拖动窗口(双击最大化/还原),用 `DragToResizeArea` 包裹窗口内容即可从边缘和四角调整大小:
4050

4151
```dart
4252
DragToResizeArea(
@@ -61,8 +71,8 @@ DragToResizeArea(
6171
```dart
6272
import 'package:flutter/src/foundation/_features.dart' show isWindowingEnabled;
6373
import 'package:flutter/src/widgets/_window.dart' as fw;
64-
import 'package:nativeapi/nativeapi.dart';
65-
import 'package:nativeapi/windowing.dart';
74+
import 'package:nativeapi_flutter/nativeapi_flutter.dart';
75+
import 'package:nativeapi_flutter/windowing.dart';
6676
6777
// 在 WidgetsFlutterBinding.ensureInitialized() 之前:stable 没有
6878
// `flutter config --enable-windowing`,所以由应用自己打开这个开关。
@@ -80,7 +90,7 @@ window?.titleBarStyle = TitleBarStyle.hidden;
8090
window?.isAlwaysOnTop = true;
8191
```
8292

83-
所有窗口共用一个 engine 和一个 isolate,窗口之间直接通过普通 Dart 对象通信——不需要改 runner,也不需要消息通道。Flutter 的多窗口 API 仍是实验性的、属于框架内部接口,因此这个桥接单独放在 `package:nativeapi/windowing.dart` 里。它针对 **stable** channel 编写(已在 3.47.5 上验证);stable 不提供 `flutter config --enable-windowing`,所以示例在 `main()` 里直接打开 Flutter 内部的 `isWindowingEnabled`。子窗口的完整例子见 [`floating_toolbar_example`](../../examples/flutter_floating_toolbar_example),另见 [`browser_tabs_example`](../../examples/flutter_browser_tabs_example) 和 [`detachable_window_example`](../../examples/flutter_detachable_window_example)。
93+
所有窗口共用一个 engine 和一个 isolate,窗口之间直接通过普通 Dart 对象通信——不需要改 runner,也不需要消息通道。Flutter 的多窗口 API 仍是实验性的、属于框架内部接口,因此这个桥接单独放在 `package:nativeapi_flutter/windowing.dart` 里。它针对 **stable** channel 编写(已在 3.47.5 上验证);stable 不提供 `flutter config --enable-windowing`,所以示例在 `main()` 里直接打开 Flutter 内部的 `isWindowingEnabled`。子窗口的完整例子见 [`floating_toolbar_example`](../../examples/flutter_floating_toolbar_example),另见 [`browser_tabs_example`](../../examples/flutter_browser_tabs_example) 和 [`detachable_window_example`](../../examples/flutter_detachable_window_example)。
8494

8595
## 示例
8696

‎bindings/dart/README.md‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,26 @@ English | [简体中文](./README-ZH.md)
1414

1515
| Package | What it is |
1616
| --- | --- |
17-
| [`nativeapi`](nativeapi) | The API: windows, tray icons, menus, displays, keyboard, dialogs, storage and more. |
18-
| [`cnativeapi`](cnativeapi) | Raw FFI bindings to core's C ABI and the native build; used by `nativeapi`. |
19-
| [`nativeapi_flutter`](nativeapi_flutter) | The Flutter-facing package. For now it re-exports `nativeapi`. |
17+
| [`nativeapi`](nativeapi) | The API: windows, tray icons, menus, displays, keyboard, dialogs, storage and more. Plain Dart, usable without Flutter. |
18+
| [`cnativeapi`](cnativeapi) | Raw FFI bindings to core's C ABI; its build hook compiles core. Used by `nativeapi`. |
19+
| [`nativeapi_flutter`](nativeapi_flutter) | For Flutter apps: re-exports `nativeapi` and adds widgets, `dart:ui` conversions and the multi-window bridge. |
2020

2121
## Installation
2222

23+
A Flutter app:
24+
2325
```bash
24-
flutter pub add nativeapi
26+
flutter pub add nativeapi_flutter
2527
```
2628

29+
A Dart app (command line, or any other Dart host):
30+
31+
```bash
32+
dart pub add nativeapi
33+
```
34+
35+
`nativeapi` has its own `Point`, `Size`, `Rectangle` and `Color` types. `nativeapi_flutter` converts them to and from `dart:ui` (`window.bounds.toRect()`, `Offset(10, 20).toNative()`), and leaves out the nativeapi names that Flutter already uses (`Brightness`, `Color`, `Display`, `Image`, `ModifierKey`, `ShortcutManager`, `Size`); import `package:nativeapi/nativeapi.dart` with a prefix to name one of those.
36+
2737
## Quick Start
2838

2939
```dart
@@ -36,7 +46,7 @@ for (final display in DisplayManager.instance.getAll()) {
3646

3747
### Custom window chrome
3848

39-
Wrap a custom title bar in `DragToMoveArea` to move the window by dragging (double tap to maximize/restore), and the window content in `DragToResizeArea` to resize from its edges and corners:
49+
With `package:nativeapi_flutter/nativeapi_flutter.dart`, wrap a custom title bar in `DragToMoveArea` to move the window by dragging (double tap to maximize/restore), and the window content in `DragToResizeArea` to resize from its edges and corners:
4050

4151
```dart
4252
DragToResizeArea(
@@ -61,8 +71,8 @@ Both widgets use `WindowManager.instance.getCurrent()` unless a `window` is pass
6171
```dart
6272
import 'package:flutter/src/foundation/_features.dart' show isWindowingEnabled;
6373
import 'package:flutter/src/widgets/_window.dart' as fw;
64-
import 'package:nativeapi/nativeapi.dart';
65-
import 'package:nativeapi/windowing.dart';
74+
import 'package:nativeapi_flutter/nativeapi_flutter.dart';
75+
import 'package:nativeapi_flutter/windowing.dart';
6676
6777
// Before WidgetsFlutterBinding.ensureInitialized(): stable has no
6878
// `flutter config --enable-windowing`, so the app switches the API on itself.
@@ -80,7 +90,7 @@ window?.titleBarStyle = TitleBarStyle.hidden;
8090
window?.isAlwaysOnTop = true;
8191
```
8292

83-
All windows share one engine and one isolate, so they talk to each other through ordinary Dart objects — no runner changes, no message channels. Flutter's multi-window API is experimental and internal to the framework, which is why the bridge lives in its own library, `package:nativeapi/windowing.dart`. It is written against the **stable** channel (checked with 3.47.5); stable does not offer `flutter config --enable-windowing`, so the examples set Flutter's internal `isWindowingEnabled` in `main()`. See [`floating_toolbar_example`](../../examples/flutter_floating_toolbar_example) for a child window built this way, and [`browser_tabs_example`](../../examples/flutter_browser_tabs_example) and [`detachable_window_example`](../../examples/flutter_detachable_window_example).
93+
All windows share one engine and one isolate, so they talk to each other through ordinary Dart objects — no runner changes, no message channels. Flutter's multi-window API is experimental and internal to the framework, which is why the bridge lives in its own library, `package:nativeapi_flutter/windowing.dart`. It is written against the **stable** channel (checked with 3.47.5); stable does not offer `flutter config --enable-windowing`, so the examples set Flutter's internal `isWindowingEnabled` in `main()`. See [`floating_toolbar_example`](../../examples/flutter_floating_toolbar_example) for a child window built this way, and [`browser_tabs_example`](../../examples/flutter_browser_tabs_example) and [`detachable_window_example`](../../examples/flutter_detachable_window_example).
8494

8595
## Examples
8696

0 commit comments

Comments
 (0)