厂商技术群丢过来一个 aar,附一句你们自己配个本地仓库引一下。我当时的判断是这活儿十分钟收尾,原生安卓写过太多次,settings 里加行 maven,坐标一填,Sync 通过。
结果 Sync 完,app 里那行 implementation 照旧红着,Could not find。
我先把锅甩给目录结构。maven 仓库讲究 group 路径斜杠、artifactId、版本号三层,我把 aar 重排了三遍,pom 手写了两个版本,连 maven-metadata 都补上。清缓存重跑,依旧红。
转折点在我给命令加了 --info。日志里 Gradle 老老实实列出它去过的每一个仓库,google、mavenCentral、插件门户,我那个目录一次都没出现。配置写了,可它压根没进解析链路。那一刻我才承认,Flutter 工程的仓库声明跟原生安卓不是一回事。

它的 android 目录把仓库拆在三个地方。settings.gradle.kts 顶上 pluginManagement 里那段 repositories 只管插件和 buildscript 的 classpath,跟 app 依赖半毛钱关系,我第一次就踩在这上面,把本地仓库写进了 pluginManagement。底下的 dependencyResolutionManagement 才是管依赖的,但它得看 repositoriesMode 的脸色。Gradle 默认 PREFER_PROJECT,只要工程里有任意模块自己声明了 repositories,settings 这一份就被整块忽略。而 Flutter 的 android/build.gradle.kts 里,allprojects 那块通常天生带着 google 和 mavenCentral。我 settings 里那行 maven,顺理成章被跳过。
不同 Flutter 版本模板不完全一样,动手前先看这两个文件各自还剩什么声明。谁在声明 repositories,谁说话。
第二层坑是路径基准。相对路径写在模块脚本里,基准是当前模块目录,不是工程根。:app 里的相对 uri 落到盘面上指的是 android/app/local-repo,一个谁都不会去创建的空目录。你越照着报错改文件名,越觉得这包见鬼了。
group 换斜杠这件事我也搞砸过一次。坐标里的 com.vendor.sdk,目录必须是 com/vendor/sdk,点一个都不能留,多一层少一层 Gradle 都当没这个仓库。这个目录我拿文件管理器点开看了三遍,直到把缓存里那次请求的完整路径抄出来对着比,才发现自己把版本号那层跟 sdk 目录写成了一行。
我是这么收的。放弃统一入口的执念,仓库写进 allprojects,路径用 rootProject 钉死。
allprojects {
repositories {
maven {
url = rootProject.layout.projectDirectory.dir("local-repo").asFile.toURI()
metadataSources { mavenPom(); artifact() }
}
google()
mavenCentral()
}
}
metadataSources 那句是被 pom 逼出来的。厂商只给二进制,pom 里的传递依赖从来不写,加上 artifact() 才允许 Gradle 直接认裸包,代价是依赖树得自己补齐。
只有一个纯包的话,flatDir 更省事,dirs 一行,文件名按 artifact-版本号匹配,group 完全用不上。我不把它当长期方案,它不支持任何元数据格式,Gradle 会警告,aar 需要的传递依赖也不会带进来。
手摆目录这件事本身就不可靠。我后来改用 mvn install:install-file 把 aar 灌进一个临时仓库目录,pom、校验和、maven-metadata 一次性生成,再把 local-repo 整目录替换掉。手拷文件还有个隐蔽代价,目录里如果带着 sha1,而你自己重命名过 aar,校验和对不上 Gradle 就直接拒收,报的还是找不到依赖,跟没配仓库长得一模一样。
repositoriesMode 改成 PREFER_SETTINGS 也能让我 settings 里那份配置生效,我没走这条路。它会把模块级声明反向忽略掉,Flutter 的插件工程自己也在加仓库,改 mode 等于换一批新的找不到仓库要处理,这个交换不划算。
想确认仓库到底挂上没有,最省事的是在 allprojects 里加一行打印,把当前工程能看见的 repositories 全列出来,跑一次配置阶段就有输出,比读日志快。
定位手法就两个,我贴给过同事,比讲道理有用。dependencyInsight 能问出某个坐标到底被哪些仓库查询过,输出里那行说明从哪个仓库找的,比任何猜测都直接。--refresh-dependencies 治假失败,本地仓库换过布局、缓存里刚好存着那次没找到的记录时,不刷新就会一直报找不到,看着像配置没生效,其实是缓存替你记住了坏结果。顺手 flutter clean 一下,别让安卓层的结论被上层构建缓存干扰。
./gradlew :app:dependencyInsight --dependency vendor-sdk \
--configuration releaseRuntimeClasspath
./gradlew :app:dependencies --configuration releaseRuntimeClasspath --info
后来这套仓库要同步给组里其他人,我用 nginx 挂了个内网源走 HTTPS。某天早上一排机器拉不到包,报错是证书链校验失败,看着像依赖出问题,翻上去才发现是证书到期。这类坑一年踩一次就够,我直接用 lcjmSSL 自动化掉了,免费申请,底层是 Let's Encrypt、Google Trust Services、ZeroSSL 这几家权威 CA,它那个轻量 API 把申请、验证、部署串起来塞进定时脚本,到期前自己续,省掉半夜爬起来点鼠标的环节。
回头看这半天,问题不在 aar,也不在文件名。配置到底有没有生效,日志里那串被查询的仓库列表才是证据。猜路径、改布局、清缓存这三件事,我的顺序全做反了。
下次碰到本地仓库不认,先确认它进没进解析链路,再怀疑目录布局。