阿里 AgenUI 开源库实战教程 Day3 附录—— Flutter Android 端源码依赖集成详解
本文聚焦 Flutter 项目的 Android 原生层,完整演示如何通过
settings.gradle.kts与app/build.gradle.kts的双文件联动,实现 AGenUI SDK 的源码依赖与 AAR 自动构建两种模式的无缝切换。
0. 目标与工程结构
今日目标
| 事项 | 内容 |
|---|---|
| 工程配置 | settings.gradle.kts + app/build.gradle.kts 双文件联动 |
| SDK 引入 | 支持源码依赖与 AAR 自动构建两种模式 |
| 依赖补全 | AGenUI 运行所需的图表、动画、Markdown 等第三方库 |
| 编译验证 | flutter run 正常通过,控制台输出 AAR 拷贝日志 |
工程目录结构
AiTestProject/
├── AGenUI-main/ # AGenUI 源码仓库
│ └── platforms/
│ └── android/ # Android SDK 源码(含 gradlew / gradlew.bat)
└── flutter/
└── demo/ # Flutter 项目
└── android/
├── settings.gradle.kts # 模块声明与仓库配置
└── app/
└── build.gradle.kts # 自动构建 + 依赖注入(核心配置)
一、前提条件
| 组件 | 版本要求 |
|---|---|
| Android Studio | Hedgehog 或更高版本 |
| Android API | 21+ (Android 5.0) |
| JDK | 11 |
| Android NDK | 25.2.9519653(如需源码构建) |
⚠️ 脚本执行提示:本文涉及
./gradlew与gradlew.bat命令,Windows 用户请在 Git Bash、WSL、PowerShell 或 macOS/Linux 终端中执行,CMD 直接运行.sh脚本可能会失败。
二、根目录配置:settings.gradle.kts
在 android/settings.gradle.kts 中配置 Flutter 插件管理、仓库地址,并通过 agenui.sdk.source 开关控制 AGenUI 模块的引入方式:
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven { url = uri("https://storage.flutter-io.cn/download.flutter.io") }
maven { url = uri("https://jitpack.io") }
}
}
/**
* AGenUI SDK 依赖模式配置
* - true : 源码依赖(引入 :agenui-sdk 模块,适合 SDK 联调)
* - false: AAR 自动构建(首次编译时自动构建 SDK 并拷贝 AAR)
*/
val agenuiSdkSource = providers.gradleProperty("agenui.sdk.source").getOrElse("false").toBoolean()
if (agenuiSdkSource) {
include(":agenui-sdk")
project(":agenui-sdk").projectDir = file("../../AGenUI-main/platforms/android")
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")
关键说明:
agenui.sdk.source = true时,通过include(":agenui-sdk")将 AGenUI Android 源码作为兄弟模块引入 Flutter 工程,修改 SDK 源码后可直接生效。agenui.sdk.source = false时,不引入模块,由app/build.gradle.kts负责自动构建 AAR 并注入依赖。
三、App 模块配置:app/build.gradle.kts
在 android/app/build.gradle.kts 中实现自动构建 + 依赖注入的完整逻辑:
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}
// ===== AGenUI SDK 模式开关 =====
val agenuiSdkSource = providers.gradleProperty("agenui.sdk.source").getOrElse("false").toBoolean()
// 路径配置:指向 AGenUI 源码目录(根据实际项目层级调整)
val agenuiSdkDir = rootProject.projectDir
.parentFile.parentFile.parentFile // 退到 flutter/demo/
.resolve("AGenUI-main/platforms/android") // 进入 SDK 目录
val agenuiAarOutputDir = agenuiSdkDir.resolve("build/outputs/aar")
val agenuiAarTargetDir = projectDir.resolve("libs")
val agenuiAarFileName = "AGenUI-Client-Android-release.aar"
val agenuiAarFile = agenuiAarTargetDir.resolve(agenuiAarFileName)
// ===== AAR 自动构建逻辑(仅在非源码模式下执行) =====
if (!agenuiSdkSource) {
agenuiAarTargetDir.mkdirs()
// 首次编译:若 AAR 不存在,自动触发 SDK 构建
if (!agenuiAarFile.exists()) {
println("[AGenUI] 首次构建:AAR 文件不存在,正在构建 SDK...")
if (!agenuiSdkDir.exists()) {
throw GradleException("[AGenUI] SDK 目录不存在: ${agenuiSdkDir.absolutePath}")
}
// 区分 Windows / macOS / Linux 的 gradlew 文件
val isWindows = System.getProperty("os.name").lowercase().contains("win")
val gradlewFile = if (isWindows) agenuiSdkDir.resolve("gradlew.bat")
else agenuiSdkDir.resolve("gradlew")
if (!gradlewFile.exists()) {
throw GradleException("[AGenUI] gradlew 不存在: ${gradlewFile.absolutePath}")
}
// 执行 SDK 的 assembleRelease
exec {
workingDir = agenuiSdkDir
if (isWindows) {
commandLine("cmd", "/c", "gradlew.bat", "assembleRelease")
} else {
commandLine("./gradlew", "assembleRelease")
}
}
// 拷贝产物到 app/libs/
if (agenuiAarOutputDir.exists()) {
agenuiAarOutputDir.listFiles()?.forEach { file ->
if (file.name.endsWith("-release.aar")) {
file.copyTo(agenuiAarFile, overwrite = true)
println("[AGenUI] AAR 已拷贝: ${agenuiAarFile.absolutePath}")
}
}
}
}
// 手动触发任务:./gradlew :app:buildSdkAar
tasks.register("buildSdkAar") {
group = "agenui"
description = "构建 AGenUI SDK 的 AAR 文件"
doLast {
exec {
workingDir = agenuiSdkDir
if (System.getProperty("os.name").lowercase().contains("win")) {
commandLine("cmd", "/c", "gradlew.bat", "assembleRelease")
} else {
commandLine("./gradlew", "assembleRelease")
}
}
}
}
// 手动触发任务:./gradlew :app:copySdkAar
tasks.register("copySdkAar", Copy::class) {
group = "agenui"
description = "拷贝 AGenUI SDK 的 AAR 文件到 app/libs"
dependsOn("buildSdkAar")
from(agenuiAarOutputDir)
include("*-release.aar")
into(agenuiAarTargetDir)
rename { agenuiAarFileName }
}
}
android {
namespace = "com.example.demo"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
applicationId = "com.example.demo"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("debug")
}
}
}
dependencies {
// ===== AGenUI SDK 依赖 =====
if (agenuiSdkSource) {
// 源码依赖模式(联调 SDK 源码时使用)
implementation(project(":agenui-sdk"))
} else {
// AAR 文件依赖(自动构建产物)
implementation(files(agenuiAarFile))
}
// ===== AGenUI 运行所需第三方库 =====
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0") // 图表
implementation("com.airbnb.android:lottie:6.1.0") // 动画
implementation("io.noties.markwon:core:4.6.2") // Markdown 渲染
implementation("io.noties.markwon:ext-tables:4.6.2")
implementation("io.noties.markwon:html:4.6.2")
implementation("com.squareup.picasso:picasso:2.8") // 图片加载
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.activity:activity:1.8.2")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.code.gson:gson:2.10.1") // JSON 解析
implementation("com.journeyapps:zxing-android-embedded:4.3.0") // 二维码扫描
implementation("com.google.zxing:core:3.5.1")
}
flutter {
source = "../.."
}
四、模式切换:gradle.properties
在 android/gradle.properties 中添加一行开关:
# false = AAR 自动构建(默认,适合日常开发)
# true = 源码依赖(修改 SDK 源码后实时生效,适合深度联调)
agenui.sdk.source=false
| 模式 | 配置值 | 适用场景 | 特点 |
|---|---|---|---|
| AAR 自动构建 | false |
日常开发、CI/CD | 首次编译自动构建 SDK,产物复用;与 SDK 源码解耦 |
| 源码依赖 | true |
SDK 深度联调、二次开发 | 直接引用 :agenui-sdk 模块,修改源码后 Flutter 热重载生效 |
五、编译验证
5.1 首次编译(AAR 自动构建模式)
cd flutter/demo
flutter pub get
cd android
# 查看 AGenUI 相关 Gradle 任务
./gradlew tasks --group=agenui
# 手动触发构建(可选,首次 flutter run 会自动执行)
./gradlew :app:copySdkAar
cd ..
flutter run
预期控制台输出:
[AGenUI] 首次构建:AAR 文件不存在,正在构建 SDK...
[AGenUI] AAR 已拷贝: /path/to/demo/android/app/libs/AGenUI-Client-Android-release.aar
5.2 切换源码模式验证
# 修改 gradle.properties
echo "agenui.sdk.source=true" >> android/gradle.properties
flutter clean
flutter run
此时应能在 Android Studio 的 Project 视图中看到 :agenui-sdk 模块,直接修改其源码即可实时生效。
六、常见问题
Q1:Windows 下执行 ./gradlew 提示找不到命令?
A:Flutter Android 目录下请使用 gradlew.bat(CMD)或 ./gradlew(Git Bash)。AGenUI SDK 目录下的 build.sh 为 Shell 脚本,请在 Git Bash 或 WSL 中执行。
Q2:提示 SDK 目录不存在?
A:检查 agenuiSdkDir 的路径计算是否正确。本文假设项目结构为 AiTestProject/flutter/demo/android/app/build.gradle.kts,若你的目录层级不同,请调整 parentFile 的调用次数。
Q3:flutter run 时提示 AAR 冲突?
A:确保 agenui.sdk.source 开关前后一致,不要同时存在 implementation(project(":agenui-sdk")) 与 implementation(files(...))。
七、小结
| 完成项 | 状态 |
|---|---|
settings.gradle.kts 配置 SDK 模块 / AAR 开关 |
✅ |
app/build.gradle.kts 自动构建 + 依赖注入 |
✅ |
支持 Windows gradlew.bat / Unix ./gradlew 双平台 |
✅ |
| AGenUI 运行所需第三方库就位 | ✅ |
gradle.properties 一键切换源码 / AAR 模式 |
✅ |
flutter run 编译通过 |
✅ |
下一步:Day 4 将基于已集成的 Android 原生层,实现 Dart 端 AGenUI 初始化与调用封装,并对接后端 SSE 流式对话接口。
更多推荐

所有评论(0)