本文聚焦 Flutter 项目的 Android 原生层,完整演示如何通过 settings.gradle.ktsapp/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(如需源码构建)

⚠️ 脚本执行提示:本文涉及 ./gradlewgradlew.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 流式对话接口。

更多推荐