Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d80bf6f
feat(bindings): add Flutter plugin scaffold (PR1)
Ankush523 Oct 18, 2026
bab060a
fix(bindings): flutter PR1 review — engine thread, narrow native sources
Ankush523 Oct 18, 2026
57e9937
fix(bindings): flutter plugin lifecycle and shared runtime config
Ankush523 Oct 18, 2026
4abed44
fix(bindings): improve Flutter plugin lifecycle handling and error ma…
Ankush523 Oct 18, 2026
6a5ca51
fix(bindings): enhance Flutter plugin lifecycle management and error …
Ankush523 Oct 18, 2026
5eb293d
chore(bindings): update Flutter analysis options and add pubspec.lock…
Ankush523 Oct 18, 2026
fb65957
fix(uniffi): point iOS config guards at ProtocolConfigParser
Ankush523 Oct 19, 2026
b8423bb
feat(bindings): add min and max imports to InitialRuntimeConfigApplie…
Ankush523 Oct 19, 2026
feb53bd
feat(bindings): Flutter events, sendMessage, and Internet transport (…
Ankush523 Oct 19, 2026
04f708d
feat(bindings): Flutter PR3 BLE, Wi‑Fi Direct, event replay, lifecycl…
Ankush523 Oct 19, 2026
edd9d8e
feat(bindings): Flutter CI, bridge docs, and workflow guards (PR4) (#…
Ankush523 Oct 19, 2026
6f99d55
chore(uniffi): rustfmt flutter guard module and lib.rs
Ankush523 Oct 19, 2026
a02be66
fix(bindings): Flutter iOS BridgeRN pod sync and example smoke config
Ankush523 Oct 21, 2026
c927dc8
docs(bindings): rewrite Flutter READMEs for production tone
Ankush523 Oct 21, 2026
9e58b47
merge: integrate origin/main into feat/flutter-binding-pr1-scaffold
Ankush523 Oct 21, 2026
e24c117
chore(deps): bump rustls-webpki and webpki-roots for RUSTSEC-2026-038…
Ankush523 Oct 21, 2026
2c892d5
chore: update webpki-roots and rustls-webpki versions in THIRD-PARTY-…
Ankush523 Oct 21, 2026
77b44e6
chore(deps): bump rustls-webpki and webpki-roots in the fuzzer lock
bahdotsh Oct 21, 2026
9f29b20
fix(bindings): restore what the config extraction dropped
bahdotsh Oct 21, 2026
51bc6eb
fix(bindings): make the Flutter plugin build what it claims to
bahdotsh Oct 21, 2026
978791e
fix(bindings): share one Flutter engine thread across plugin instances
bahdotsh Oct 21, 2026
683d817
fix(bindings): validate a Flutter config before tearing the engine down
bahdotsh Oct 21, 2026
431b918
test(bindings): cover malformed events and the deferred cap in Dart
bahdotsh Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1327,3 +1327,130 @@ jobs:
- name: Build a minified application against the published library
working-directory: bindings/kotlin/consumer-check
run: gradle --no-daemon --stacktrace -PVERSION_NAME=0.0.0 :app:assembleRelease

flutter-binding:
name: Flutter Binding
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
with:
# paths-filter compares the PR diff to the base branch.
fetch-depth: 0

# Native AAR gate: structural/UniFFI paths or lockfile (engine-only PRs skip
# NDK/Gradle; pushes to main always run the full job).
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: flutter_paths
with:
filters: |
flutter_dart:
- 'bindings/flutter/**'
- 'docs/bridges/flutter.md'
- '.github/workflows/ci.yml'
- 'crates/offline-protocol-uniffi/src/tests/flutter_bridge_guards.rs'
flutter_native:
# The plugin's own Kotlin and Gradle build: `flutter analyze`
# excludes android/**, so without these a pull request that
# touches only them never compiles them, and main is where it
# breaks.
- 'bindings/flutter/offline_protocol/android/**'
- 'bindings/flutter/offline_protocol/example/android/**'
- 'bindings/flutter/baselines/**'
- 'bindings/flutter/scripts/**'
- 'bindings/react-native/android/**'
- 'scripts/generate-bindings.sh'
- 'crates/offline-protocol-uniffi/Cargo.toml'
- 'crates/offline-protocol-uniffi/build.rs'
- 'crates/offline-protocol-uniffi/src/lib.rs'
- 'crates/offline-protocol-uniffi/src/offline_protocol.udl'
- 'crates/offline-protocol-uniffi/src/capi/**'
- 'crates/offline-protocol-uniffi/src/host_log.rs'
flutter_lock:
- 'Cargo.lock'
- 'Cargo.toml'

- name: Decide Flutter native (NDK + AAR) gate
id: flutter_native_gate
run: |
run=false
if [[ "${{ github.event_name }}" == "push" ]]; then
run=true
elif [[ "${{ steps.flutter_paths.outputs.flutter_native }}" == "true" ]]; then
run=true
elif [[ "${{ steps.flutter_paths.outputs.flutter_lock }}" == "true" ]]; then
run=true
fi
echo "run=$run" >> "$GITHUB_OUTPUT"

- name: Skip (no Flutter-related changes on this PR)
if: >-
github.event_name == 'pull_request'
&& steps.flutter_paths.outputs.flutter_dart != 'true'
&& steps.flutter_paths.outputs.flutter_native != 'true'
&& steps.flutter_paths.outputs.flutter_lock != 'true'
run: echo "No matching paths; Flutter Binding checks skipped for this PR."

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
if: >-
github.event_name == 'push'
|| steps.flutter_paths.outputs.flutter_dart == 'true'
|| steps.flutter_paths.outputs.flutter_native == 'true'
|| steps.flutter_paths.outputs.flutter_lock == 'true'
with:
channel: stable
flutter-version: "3.27.4"
cache: true

- name: Dart unit tests and analyzer
if: >-
github.event_name == 'push'
|| steps.flutter_paths.outputs.flutter_dart == 'true'
|| steps.flutter_paths.outputs.flutter_native == 'true'
|| steps.flutter_paths.outputs.flutter_lock == 'true'
working-directory: bindings/flutter/offline_protocol
run: |
flutter pub get
flutter test
flutter analyze

- uses: actions/setup-java@v6
if: steps.flutter_native_gate.outputs.run == 'true'
with:
distribution: temurin
java-version: "17"

- name: Setup Android NDK
if: steps.flutter_native_gate.outputs.run == 'true'
uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
id: setup-ndk
with:
ndk-version: r26b
add-to-path: true

- uses: dtolnay/rust-toolchain@stable
if: steps.flutter_native_gate.outputs.run == 'true'
with:
targets: aarch64-linux-android

- uses: Swatinem/rust-cache@v2
if: steps.flutter_native_gate.outputs.run == 'true'
with:
shared-key: ci-flutter-binding

- name: Build arm64 UniFFI for Flutter Android
if: steps.flutter_native_gate.outputs.run == 'true'
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
run: bash bindings/flutter/scripts/ci-build-android-jni-arm64.sh

# The example's Gradle wrapper is committed and the next step runs it,
# so its jar is checked against Gradle's published checksums first.
- uses: gradle/actions/wrapper-validation@v6
if: steps.flutter_native_gate.outputs.run == 'true'

- name: Assemble release AAR and check size baseline
if: steps.flutter_native_gate.outputs.run == 'true'
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
run: bindings/flutter/scripts/measure-plugin-aar-size.sh --check
14 changes: 13 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,19 @@ jobs:
exit 1
fi

echo "Workspace and Python versions ($CARGO_VERSION) match tag v$TAG_VERSION."
# The Flutter plugin's podspec and Gradle build read the React Native
# package.json, but pub has no way to read another file, so the
# pubspec carries the number itself and is checked here. Without
# this a release ships a plugin that says the previous version.
FLUTTER_VERSION="$(grep -m1 '^version: ' bindings/flutter/offline_protocol/pubspec.yaml \
| sed 's/^version: *//' || true)"

if [ "$TAG_CORE" != "$FLUTTER_VERSION" ]; then
echo "::error title=Flutter plugin version does not match the tag::Tag v$TAG_VERSION wants $TAG_CORE, but bindings/flutter/offline_protocol/pubspec.yaml reads '${FLUTTER_VERSION}'. It moves with the workspace version: bump it, then delete and re-push the tag (nothing has published yet)."
exit 1
fi

echo "Workspace, Python and Flutter versions ($CARGO_VERSION) match tag v$TAG_VERSION."

# ---------------------------------------------------------------------------
# Release
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ archived by series under [docs/changelog/](docs/changelog/); see the

### Added

- **A Flutter plugin (preview).** `bindings/flutter/offline_protocol` hosts
the engine on iOS and Android from Dart: create, MLS with the platform
secure stores, start, stop, destroy, the local address, `sendMessage`, the
Internet, BLE and Wi-Fi Direct transports, and core events with the same
sequence numbers and gap replay as React Native. Its Android and iOS hosts
compile the React Native bridge sources where they are, less a named list
of the ones that need React, so a transport fix reaches both. Nostr and
Reticulum are not hosted yet, and a config that enables either is refused
at create. Until it is on pub.dev it is a path dependency. CI runs the Dart
tests and assembles the Android plugin against a size baseline; nothing in
CI compiles the iOS plugin yet. The contract is `docs/bridges/flutter.md`.
- **A Java binding.** `bindings/java` is a typed Java API generated from the
interface definition by `uniffi-bindgen-java`, the fourth member of the
artifact set that `scripts/generate-bindings.sh` writes with Swift, Kotlin
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ Version files to bump together:
| `Cargo.lock` | any `cargo` command refreshes it once the manifests change |
| `tools/embedded-footprint/Cargo.lock`, `tools/mls-interop/Cargo.lock` | Separate workspaces that depend on the SDK crates by path, so their lockfiles pin the versions too. CI builds both with `--locked`, so a stale one fails the run. `cargo metadata --manifest-path <tool>/Cargo.toml` refreshes each |
| `bindings/python/pyproject.toml` | `version` — release.yml gates this one against the tag too |
| `bindings/react-native/package.json` + `package-lock.json` | `npm version X.Y.Z --no-git-tag-version --allow-same-version` from `bindings/react-native/` |
| `bindings/react-native/package.json` + `package-lock.json` | `npm version X.Y.Z --no-git-tag-version --allow-same-version` from `bindings/react-native/`. The Flutter plugin's podspec and Gradle build read their version from this file |
| `bindings/flutter/offline_protocol/pubspec.yaml` | `version`. Pub cannot read another file, so this one carries the number itself, and release.yml gates it against the tag |
| `THIRD-PARTY-NOTICES.md` (×3) | `scripts/generate-third-party-notices.sh` — the notices list our own crates by version, and the CI drift gate fails on a stale copy |

Then the prose: `CHANGELOG.md` (replace `## [Unreleased]` with
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Every channel carries the version number of the release it came from.
| Swift, iOS (preview) | [`offline-protocol-swift`](https://github.com/Offline-Protocol/offline-protocol-swift), product `OfflineProtocolSDK` | Swift Package Manager, see below |
| Android, Kotlin (preview) | A download on each [GitHub release](https://github.com/Offline-Protocol/offline-protocol-sdk/releases); not on Maven Central yet | See below |
| C, and any language that calls C | `offline_protocol.h` and the native library in each [GitHub release](https://github.com/Offline-Protocol/offline-protocol-sdk/releases)'s desktop archives | See [bindings/c](bindings/c/README.md) |
| Flutter (iOS, Android, preview) | [`offline_protocol`](bindings/flutter/README.md) plugin in this repository | Path dependency; see [bindings/flutter](bindings/flutter/README.md) |
| Java (JVM 22+, desktop and server) | Generated sources in [bindings/java](bindings/java/README.md), over the native library in each release's desktop archives; not on Maven Central yet | See [bindings/java](bindings/java/README.md) |

**Python.** Wheels are published for Python 3.10 or later on macOS arm64
Expand Down
4 changes: 2 additions & 2 deletions THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ DEALINGS IN THE SOFTWARE.

Used by:

- [webpki-roots 1.0.9](https://github.com/rustls/webpki-roots)
- [webpki-roots 1.0.10](https://github.com/rustls/webpki-roots)

```
# Community Data License Agreement - Permissive - Version 2.0
Expand Down Expand Up @@ -949,7 +949,7 @@ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Used by:

- [rustls-webpki 0.103.15](https://github.com/rustls/webpki)
- [rustls-webpki 0.103.16](https://github.com/rustls/webpki)

```
Except as otherwise noted, this project is licensed under the following
Expand Down
14 changes: 14 additions & 0 deletions bindings/flutter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.dart_tool/
.packages
build/
**/example/build/
**/example/.dart_tool/
**/example/.flutter-plugins-dependencies
**/example/ios/Flutter/
**/example/ios/Pods/
**/example/ios/.symlinks/
**/example/ios/Runner/GeneratedPluginRegistrant.h
**/example/ios/Runner/GeneratedPluginRegistrant.m
**/example/android/.gradle/
**/example/android/local.properties
**/example/android/app/src/main/java/io/flutter/plugins/
Loading
Loading