@ondeinference / onde / commits / ebf7715

Build the Dart SDK's Android libs with cargo-ndk

The android/ module compiled the Rust engine through CMake and then imported the result with add_library(... SHARED IMPORTED). AGP does not package imported libraries, so an APK built against the plugin shipped without libonde_inference_dart.so in it. The bare `cargo build` in that CMakeLists had no NDK toolchain wired up anyway. Replace it with a cargoBuildAndroid Exec task that runs cargo-ndk and writes into build/rustJniLibs, registered as a jniLibs source dir so AGP picks the libraries up. android.ndkDirectory is deliberately not used to locate the NDK: this is a library module with no ndkVersion of its own, so AGP resolves its own default version and fails with "NDK is not installed" even right after the app module installed a working one. Resolve it from ANDROID_NDK_HOME or the newest version under $ANDROID_HOME/ndk instead. AGP goes 7.3.0 to 8.7.3 and compileSdk 33 to 35, which the regenerated example runner needs. The onde.androidAbis property overrides which ABIs get built, since each one costs a few minutes of release-profile cargo. Also adds the example's android/ runner, which never existed, and checks sdk/dart/rust for aarch64-linux-android in CI alongside the core crate. Verified with flutter build apk --target-platform android-arm64: libonde_inference_dart.so lands in lib/arm64-v8a/ of the built APK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Seto Elkahfi committed Aug 17, 2026 at 18:31 UTC ebf77159bfd846723c7544c0bb47a6a98f817c24
25 files changed +369 -67
.github/workflows/ci.yml
+4
@@ -407,6 +407,10 @@ jobs:
407 cache-bin: false
408 key: android-arm64
409 - run: cargo check --target aarch64-linux-android
410 + # The Dart SDK's FFI crate is what actually ships in the Android AAR, so
411 + # check it too — a core change can compile while breaking the bridge.
412 + - name: Check Dart FFI crate
413 + run: cargo check --target aarch64-linux-android --manifest-path sdk/dart/rust/Cargo.toml
414
415 # ── Cross-compile checks for Windows (lowest priority) ────────────────────
416 check-windows:
sdk/dart/README.md
+22 -1
@@ -30,7 +30,7 @@ You get multi-turn chat, streaming, one-shot generation, configurable sampling,
30 |----------|---------|---------------|-------|
31 | iOS 13+ | Metal | Qwen 2.5 Coder 1.5B (~941 MB) | CocoaPods and Swift Package Manager plugin manifests are included |
32 | macOS 10.15+ | Metal | Qwen 2.5 Coder 3B (~1.93 GB) | CocoaPods and Swift Package Manager plugin manifests are included |
33 -| Android API 21+ | CPU | Qwen 2.5 Coder 1.5B (~941 MB) | arm64-v8a, armeabi-v7a, x86_64, x86 |
33 +| Android API 21+ | CPU | Qwen 2.5 Coder 1.5B (~941 MB) | arm64-v8a, armeabi-v7a, x86_64 by default; see [Android ABIs](#android-abis) |
34 | Linux x86_64 | CPU | Qwen 2.5 Coder 3B (~1.93 GB) | CUDA possible, see docs |
35 | Windows x86_64 | CPU | Qwen 2.5 Coder 3B (~1.93 GB) | CUDA possible, see docs |
36
@@ -265,6 +265,27 @@ Future<void> main() async {
265
266 ---
267
268 +## Android ABIs
269 +
270 +The Rust engine is cross-compiled during `flutter build apk` by the plugin's
271 +Gradle module, which shells out to [`cargo-ndk`](https://github.com/bbqsrc/cargo-ndk).
272 +You need it once per machine, along with the Rust targets:
273 +
274 +```bash
275 +cargo install cargo-ndk
276 +rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
277 +```
278 +
279 +The NDK comes from whichever version Gradle already resolved, so no
280 +`ANDROID_NDK_HOME` export is needed for Gradle-driven builds.
281 +
282 +By default all three ABIs are built, which is slow — the engine is a large
283 +release-profile crate. While iterating, build only the one your device needs:
284 +
285 +```bash
286 +flutter run -d <device> --android-project-arg=onde.androidAbis=arm64-v8a
287 +```
288 +
289 ## Example app
290
291 A full Flutter example lives in `example/`. It demonstrates:
sdk/dart/android/CMakeLists.txt deleted
-51
@@ -1,51 +0,0 @@
1 -cmake_minimum_required(VERSION 3.18)
2 -project(onde_inference_dart)
3 -
4 -# ── Rust source directory ─────────────────────────────────────────────────────
5 -# CMakeLists.txt lives in android/, so ../rust resolves to the sibling rust/ dir.
6 -set(RUST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../rust")
7 -
8 -# ── Map Android ABI → Cargo target triple ────────────────────────────────────
9 -if(ANDROID_ABI STREQUAL "arm64-v8a")
10 - set(CARGO_TARGET "aarch64-linux-android")
11 -elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
12 - set(CARGO_TARGET "armv7-linux-androideabi")
13 -elseif(ANDROID_ABI STREQUAL "x86_64")
14 - set(CARGO_TARGET "x86_64-linux-android")
15 -elseif(ANDROID_ABI STREQUAL "x86")
16 - set(CARGO_TARGET "i686-linux-android")
17 -else()
18 - message(FATAL_ERROR "Unsupported Android ABI: ${ANDROID_ABI}")
19 -endif()
20 -
21 -# ── Output path for the compiled Rust shared library ─────────────────────────
22 -set(LIB_OUTPUT
23 - "${RUST_DIR}/target/${CARGO_TARGET}/release/libonde_inference_dart.so"
24 -)
25 -
26 -# ── Custom build step: invoke cargo ──────────────────────────────────────────
27 -# Requires the Android NDK cross-compilation toolchain to be on PATH, or
28 -# a .cargo/config.toml in the Rust crate with the correct linker overrides
29 -# for each Android target.
30 -add_custom_command(
31 - OUTPUT ${LIB_OUTPUT}
32 - COMMAND cargo build
33 - --release
34 - --target ${CARGO_TARGET}
35 - --manifest-path "${RUST_DIR}/Cargo.toml"
36 - WORKING_DIRECTORY ${RUST_DIR}
37 - COMMENT "Building onde_inference_dart for ${CARGO_TARGET}…"
38 - VERBATIM
39 -)
40 -
41 -# Dummy target that depends on the cargo output so CMake tracks it correctly.
42 -add_custom_target(onde_inference_dart_cargo_build ALL
43 - DEPENDS ${LIB_OUTPUT}
44 -)
45 -
46 -# ── Import the built .so as a pre-built shared library ───────────────────────
47 -add_library(onde_inference_dart SHARED IMPORTED GLOBAL)
48 -set_target_properties(onde_inference_dart PROPERTIES
49 - IMPORTED_LOCATION ${LIB_OUTPUT}
50 -)
51 -add_dependencies(onde_inference_dart onde_inference_dart_cargo_build)
sdk/dart/android/build.gradle
+92 -13
@@ -7,7 +7,7 @@ buildscript {
7 mavenCentral()
8 }
9 dependencies {
10 - classpath 'com.android.tools.build:gradle:7.3.0'
10 + classpath 'com.android.tools.build:gradle:8.7.3'
11 }
12 }
13
@@ -20,26 +20,105 @@ rootProject.allprojects {
20
21 apply plugin: 'com.android.library'
22
23 +// ── ABIs to cross-compile ────────────────────────────────────────────────────
24 +// Override from the consuming app to shorten local iteration, e.g.
25 +// flutter run -d <device> --android-project-arg=onde.androidAbis=arm64-v8a
26 +def ondeAndroidAbis = (project.findProperty('onde.androidAbis') ?: 'arm64-v8a,armeabi-v7a,x86_64')
27 + .split(',')
28 + .collect { it.trim() }
29 + .findAll { !it.isEmpty() }
30 +
31 +// Lowest API level the Rust code is linked against. Must stay <= the consuming
32 +// app's minSdk, and matches defaultConfig.minSdkVersion below.
33 +def ondeAndroidApiLevel = 21
34 +
35 android {
24 - compileSdkVersion 33
36 + compileSdkVersion 35
37 + namespace 'com.ondeinference.onde_inference'
38
39 defaultConfig {
27 - minSdkVersion 21
40 + minSdkVersion ondeAndroidApiLevel
41 + }
42
29 - externalNativeBuild {
30 - cmake {
31 - cppFlags '-std=c++17'
32 - arguments '-DANDROID_STL=c++_shared'
33 - }
43 + compileOptions {
44 + sourceCompatibility JavaVersion.VERSION_17
45 + targetCompatibility JavaVersion.VERSION_17
46 + }
47 +
48 + // cargo-ndk drops libonde_inference_dart.so into <abi>/ subdirectories here.
49 + // AGP packages anything under a jniLibs source dir, which an IMPORTED CMake
50 + // library would *not* have been.
51 + sourceSets {
52 + main {
53 + jniLibs.srcDirs += ["${layout.buildDirectory.get().asFile}/rustJniLibs"]
54 }
55 }
56 +}
57 +
58 +// ── NDK location ─────────────────────────────────────────────────────────────
59 +// `android.ndkDirectory` is deliberately not used: this is a library module
60 +// with no ndkVersion of its own, so AGP resolves its own default version and
61 +// throws "NDK is not installed" even when the app module has just installed a
62 +// perfectly good NDK. Resolve it ourselves instead.
63 +def resolveNdkDir = {
64 + def fromEnv = System.getenv('ANDROID_NDK_HOME') ?: System.getenv('ANDROID_NDK_ROOT')
65 + if (fromEnv && new File(fromEnv).isDirectory()) {
66 + return new File(fromEnv)
67 + }
68
37 - externalNativeBuild {
38 - cmake {
39 - path 'CMakeLists.txt'
40 - version '3.18.1'
69 + def sdkRoot = System.getenv('ANDROID_HOME') ?: System.getenv('ANDROID_SDK_ROOT')
70 + if (sdkRoot) {
71 + def ndkRoot = new File(sdkRoot, 'ndk')
72 + def installed = ndkRoot.isDirectory() ? ndkRoot.listFiles({ it.isDirectory() } as FileFilter) : null
73 + if (installed) {
74 + // Newest installed version wins. The Rust output is a standalone
75 + // .so, so it need not match the NDK the Flutter engine was built
76 + // with — any recent one links fine.
77 + def newest = installed.toList().max { dir ->
78 + dir.name.split('\\.').collect { it.isInteger() ? it.toInteger() : 0 }
79 + }
80 + if (newest) {
81 + return newest
82 + }
83 }
84 }
85
44 - namespace 'com.ondeinference.onde_inference'
86 + throw new GradleException(
87 + 'Could not locate an Android NDK. Install one via Android Studio\'s SDK ' +
88 + 'Manager, or set ANDROID_NDK_HOME to an NDK directory.')
89 +}
90 +
91 +// ── Rust cross-compilation ───────────────────────────────────────────────────
92 +// Always built with --release: a debug-profile inference engine is too slow to
93 +// be worth running, so there is no point wiring this per build type.
94 +tasks.register('cargoBuildAndroid', Exec) {
95 + description = "Cross-compiles the onde_inference_dart crate for ${ondeAndroidAbis.join(', ')}"
96 + workingDir file('../rust')
97 +
98 + def outDir = file("${layout.buildDirectory.get().asFile}/rustJniLibs")
99 +
100 + // Gradle launched from Android Studio does not necessarily inherit the
101 + // shell PATH, so fall back to the standard rustup install location.
102 + def cargoBin = 'cargo'
103 + def rustupCargo = new File("${System.getProperty('user.home')}/.cargo/bin/cargo")
104 + if (rustupCargo.exists()) {
105 + cargoBin = rustupCargo.absolutePath
106 + }
107 +
108 + def cargoArgs = [cargoBin, 'ndk']
109 + ondeAndroidAbis.each { cargoArgs += ['-t', it] }
110 + cargoArgs += ['-P', ondeAndroidApiLevel.toString(), '-o', outDir.absolutePath, 'build', '--release']
111 + commandLine cargoArgs
112 +
113 + doFirst {
114 + outDir.mkdirs()
115 + // cargo-ndk locates the toolchain via ANDROID_NDK_HOME.
116 + environment 'ANDROID_NDK_HOME', resolveNdkDir().absolutePath
117 + }
118 +}
119 +
120 +tasks.configureEach { task ->
121 + if (task.name == 'preBuild') {
122 + task.dependsOn 'cargoBuildAndroid'
123 + }
124 }
sdk/dart/example/.metadata
+3
@@ -15,6 +15,9 @@ migration:
15 - platform: root
16 create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
17 base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
18 + - platform: android
19 + create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
20 + base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
21 - platform: ios
22 create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
23 base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
sdk/dart/example/README.md
+9 -2
@@ -58,7 +58,14 @@ flutter pub get
58 flutter run
59 ```
60
61 -On **iOS / macOS**, the Rust engine is built through the CocoaPods podspec and linked automatically by Xcode. On **Android**, it is built through CMake and Gradle. On **Linux / Windows**, it is linked as a shared library.
61 +On **iOS / macOS**, the Rust engine is built through the CocoaPods podspec and linked automatically by Xcode. On **Android**, a Gradle task cross-compiles it with `cargo-ndk` into `jniLibs`, so you need `cargo install cargo-ndk` plus the Android Rust targets. On **Linux / Windows**, it is linked as a shared library.
62 +
63 +Building all three Android ABIs takes a long time. Pass the ABI you actually
64 +need while iterating:
65 +
66 +```bash
67 +flutter run -d <device> --android-project-arg=onde.androidAbis=arm64-v8a
68 +```
69
70 ### Platform notes
71
@@ -103,7 +110,7 @@ await engine.unloadModel();
110 example/
111 ├── lib/
112 │ └── main.dart # Complete chat UI + SDK integration
106 -├── android/ # Android Gradle + CMake build
113 +├── android/ # Android Gradle build
114 ├── ios/ # iOS Xcode project + CocoaPods
115 ├── macos/ # macOS Xcode project + CocoaPods
116 ├── linux/ # Linux CMake build
sdk/dart/example/android/.gitignore new
+14
@@ -0,0 +1,14 @@
1 +gradle-wrapper.jar
2 +/.gradle
3 +/captures/
4 +/gradlew
5 +/gradlew.bat
6 +/local.properties
7 +GeneratedPluginRegistrant.java
8 +.cxx/
9 +
10 +# Remember to never publicly share your keystore.
11 +# See https://flutter.dev/to/reference-keystore
12 +key.properties
13 +**/*.keystore
14 +**/*.jks
sdk/dart/example/android/app/build.gradle.kts new
+44
@@ -0,0 +1,44 @@
1 +plugins {
2 + id("com.android.application")
3 + id("kotlin-android")
4 + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 + id("dev.flutter.flutter-gradle-plugin")
6 +}
7 +
8 +android {
9 + namespace = "com.ondeinference.onde_inference_example"
10 + compileSdk = flutter.compileSdkVersion
11 + ndkVersion = flutter.ndkVersion
12 +
13 + compileOptions {
14 + sourceCompatibility = JavaVersion.VERSION_17
15 + targetCompatibility = JavaVersion.VERSION_17
16 + }
17 +
18 + kotlinOptions {
19 + jvmTarget = JavaVersion.VERSION_17.toString()
20 + }
21 +
22 + defaultConfig {
23 + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
24 + applicationId = "com.ondeinference.onde_inference_example"
25 + // You can update the following values to match your application needs.
26 + // For more information, see: https://flutter.dev/to/review-gradle-config.
27 + minSdk = flutter.minSdkVersion
28 + targetSdk = flutter.targetSdkVersion
29 + versionCode = flutter.versionCode
30 + versionName = flutter.versionName
31 + }
32 +
33 + buildTypes {
34 + release {
35 + // TODO: Add your own signing config for the release build.
36 + // Signing with the debug keys for now, so `flutter run --release` works.
37 + signingConfig = signingConfigs.getByName("debug")
38 + }
39 + }
40 +}
41 +
42 +flutter {
43 + source = "../.."
44 +}
sdk/dart/example/android/app/src/debug/AndroidManifest.xml new
+7
@@ -0,0 +1,7 @@
1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2 + <!-- The INTERNET permission is required for development. Specifically,
3 + the Flutter tool needs it to communicate with the running application
4 + to allow setting breakpoints, to provide hot reload, etc.
5 + -->
6 + <uses-permission android:name="android.permission.INTERNET"/>
7 +</manifest>
sdk/dart/example/android/app/src/main/AndroidManifest.xml new
+45
@@ -0,0 +1,45 @@
1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2 + <application
3 + android:label="onde_inference_example"
4 + android:name="${applicationName}"
5 + android:icon="@mipmap/ic_launcher">
6 + <activity
7 + android:name=".MainActivity"
8 + android:exported="true"
9 + android:launchMode="singleTop"
10 + android:taskAffinity=""
11 + android:theme="@style/LaunchTheme"
12 + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
13 + android:hardwareAccelerated="true"
14 + android:windowSoftInputMode="adjustResize">
15 + <!-- Specifies an Android theme to apply to this Activity as soon as
16 + the Android process has started. This theme is visible to the user
17 + while the Flutter UI initializes. After that, this theme continues
18 + to determine the Window background behind the Flutter UI. -->
19 + <meta-data
20 + android:name="io.flutter.embedding.android.NormalTheme"
21 + android:resource="@style/NormalTheme"
22 + />
23 + <intent-filter>
24 + <action android:name="android.intent.action.MAIN"/>
25 + <category android:name="android.intent.category.LAUNCHER"/>
26 + </intent-filter>
27 + </activity>
28 + <!-- Don't delete the meta-data below.
29 + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
30 + <meta-data
31 + android:name="flutterEmbedding"
32 + android:value="2" />
33 + </application>
34 + <!-- Required to query activities that can process text, see:
35 + https://developer.android.com/training/package-visibility and
36 + https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
37 +
38 + In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
39 + <queries>
40 + <intent>
41 + <action android:name="android.intent.action.PROCESS_TEXT"/>
42 + <data android:mimeType="text/plain"/>
43 + </intent>
44 + </queries>
45 +</manifest>
sdk/dart/example/android/app/src/main/kotlin/com/ondeinference/onde_inference_example/MainActivity.kt new
+5
@@ -0,0 +1,5 @@
1 +package com.ondeinference.onde_inference_example
2 +
3 +import io.flutter.embedding.android.FlutterActivity
4 +
5 +class MainActivity : FlutterActivity()
sdk/dart/example/android/app/src/main/res/drawable-v21/launch_background.xml new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- Modify this file to customize your launch splash screen -->
3 +<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4 + <item android:drawable="?android:colorBackground" />
5 +
6 + <!-- You can insert your own image assets here -->
7 + <!-- <item>
8 + <bitmap
9 + android:gravity="center"
10 + android:src="@mipmap/launch_image" />
11 + </item> -->
12 +</layer-list>
sdk/dart/example/android/app/src/main/res/drawable/launch_background.xml new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- Modify this file to customize your launch splash screen -->
3 +<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
4 + <item android:drawable="@android:color/white" />
5 +
6 + <!-- You can insert your own image assets here -->
7 + <!-- <item>
8 + <bitmap
9 + android:gravity="center"
10 + android:src="@mipmap/launch_image" />
11 + </item> -->
12 +</layer-list>
sdk/dart/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files /dev/null and b/sdk/dart/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
sdk/dart/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files /dev/null and b/sdk/dart/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
sdk/dart/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files /dev/null and b/sdk/dart/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
sdk/dart/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files /dev/null and b/sdk/dart/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
sdk/dart/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files /dev/null and b/sdk/dart/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
sdk/dart/example/android/app/src/main/res/values-night/styles.xml new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<resources>
3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
4 + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
5 + <!-- Show a splash screen on the activity. Automatically removed when
6 + the Flutter engine draws its first frame -->
7 + <item name="android:windowBackground">@drawable/launch_background</item>
8 + </style>
9 + <!-- Theme applied to the Android Window as soon as the process has started.
10 + This theme determines the color of the Android Window while your
11 + Flutter UI initializes, as well as behind your Flutter UI while its
12 + running.
13 +
14 + This Theme is only used starting with V2 of Flutter's Android embedding. -->
15 + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
16 + <item name="android:windowBackground">?android:colorBackground</item>
17 + </style>
18 +</resources>
sdk/dart/example/android/app/src/main/res/values/styles.xml new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<resources>
3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
4 + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
5 + <!-- Show a splash screen on the activity. Automatically removed when
6 + the Flutter engine draws its first frame -->
7 + <item name="android:windowBackground">@drawable/launch_background</item>
8 + </style>
9 + <!-- Theme applied to the Android Window as soon as the process has started.
10 + This theme determines the color of the Android Window while your
11 + Flutter UI initializes, as well as behind your Flutter UI while its
12 + running.
13 +
14 + This Theme is only used starting with V2 of Flutter's Android embedding. -->
15 + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
16 + <item name="android:windowBackground">?android:colorBackground</item>
17 + </style>
18 +</resources>
sdk/dart/example/android/app/src/profile/AndroidManifest.xml new
+7
@@ -0,0 +1,7 @@
1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2 + <!-- The INTERNET permission is required for development. Specifically,
3 + the Flutter tool needs it to communicate with the running application
4 + to allow setting breakpoints, to provide hot reload, etc.
5 + -->
6 + <uses-permission android:name="android.permission.INTERNET"/>
7 +</manifest>
sdk/dart/example/android/build.gradle.kts new
+24
@@ -0,0 +1,24 @@
1 +allprojects {
2 + repositories {
3 + google()
4 + mavenCentral()
5 + }
6 +}
7 +
8 +val newBuildDir: Directory =
9 + rootProject.layout.buildDirectory
10 + .dir("../../build")
11 + .get()
12 +rootProject.layout.buildDirectory.value(newBuildDir)
13 +
14 +subprojects {
15 + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
16 + project.layout.buildDirectory.value(newSubprojectBuildDir)
17 +}
18 +subprojects {
19 + project.evaluationDependsOn(":app")
20 +}
21 +
22 +tasks.register<Delete>("clean") {
23 + delete(rootProject.layout.buildDirectory)
24 +}
sdk/dart/example/android/gradle.properties new
+2
@@ -0,0 +1,2 @@
1 +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
2 +android.useAndroidX=true
sdk/dart/example/android/gradle/wrapper/gradle-wrapper.properties new
+5
@@ -0,0 +1,5 @@
1 +distributionBase=GRADLE_USER_HOME
2 +distributionPath=wrapper/dists
3 +zipStoreBase=GRADLE_USER_HOME
4 +zipStorePath=wrapper/dists
5 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
sdk/dart/example/android/settings.gradle.kts new
+26
@@ -0,0 +1,26 @@
1 +pluginManagement {
2 + val flutterSdkPath =
3 + run {
4 + val properties = java.util.Properties()
5 + file("local.properties").inputStream().use { properties.load(it) }
6 + val flutterSdkPath = properties.getProperty("flutter.sdk")
7 + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
8 + flutterSdkPath
9 + }
10 +
11 + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
12 +
13 + repositories {
14 + google()
15 + mavenCentral()
16 + gradlePluginPortal()
17 + }
18 +}
19 +
20 +plugins {
21 + id("dev.flutter.flutter-plugin-loader") version "1.0.0"
22 + id("com.android.application") version "8.11.1" apply false
23 + id("org.jetbrains.kotlin.android") version "2.2.20" apply false
24 +}
25 +
26 +include(":app")