F-Droid 发布 Flutter 应用指南

发布: 2026-01-17   上次更新: 2026-01-17   分类: 理解计算机   标签: flutter fdroid

文章目录

最近开发了一款好几年前就想做的应用,一个管理 2FA(两步验证)令牌的工具,叫做 Flauth ,基于 Flutter 开发,支持全平台。也已经发布到了 F-Droid 上,欢迎大家安装使用。

在如今 Agent 盛行的年代,开发这个软件本身没什么难度,大概 80% 的代码都是 Vibe 的,Dart 也是现学现卖,剩下的 20% 着重花在了设计与测试上,之所以想自己开发,主要是市面上没有同时满足以下三点需求的 App:

  1. 支持多平台,我目前主要用到 macOS、Linux、Android
  2. 开源,最起码要支持数据的导入、导出,并且是开放的格式,没有 Vendor Lock-in 的风险
  3. 支持自定义备份,毕竟数据在自己手上才是最安全的

Flauth 就是上述需求的产物,技术细节这里不多赘述,感兴趣的读者可以查看:Flauth 身份验证架构说明备份与恢复逻辑性能设计文档

在小范围内测之后,功能已经完备,我也把 Authy 中的十几个令牌 一个个 (鄙视脸!)迁移过来,就准备发布到 F-Droid 上,之所以选择这个平台,主要是因为它是开源软件的集中地,用户群体也比较符合我的预期。但由于是第一次使用 F-Droid 发布应用,过程还是遇到了一些问题,花了一些时间才弄明白。这篇博客文章就来分享一下我的发布经验,希望能帮助到有类似需求的读者。

签名

在发布之前,需要对 apk 文件签名,这是 Android 应用发布的基本要求。出于安全考虑,应用开发者也需要签名,以防止应用被篡改。Sign the app 是 Flutter 的文档介绍。需要修改 build.gradle.kts 文件,添加签名配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
android {
    ...
    signingConfigs {
        create("release") {
            keyAlias = System.getenv("KEY_ALIAS")
            keyPassword = System.getenv("KEY_PASSWORD")
            storeFile = file(System.getenv("KEYSTORE_PATH"))
            storePassword = System.getenv("KEYSTORE_PASSWORD")
        }
    }
    buildTypes {
        release {
            signingConfig = signingConfigs.getByName("release")
            ...
        }
    }
}

Android 签名指南是 Flauth 中的签名流程,供大家参考。还有一点需要注意,AGP 默认会插入一个额外的签名块(extra signing block), 叫做 Dependency metadata,用于存储一些依赖信息。但 F-Droid 要求应用不能包含这个签名块,否则会报错,需要在 build.gradle.kts 中添加如下配置::

1
2
3
4
5
6
7
8
9
android {
    ...
    dependenciesInfo {
        // Disables dependency metadata when building APKs.
        includeInApk = false
        // Disables dependency metadata when building Android App Bundles.
        includeInBundle = false
    }
}

这样构建出来的 apk 文件就不会包含这个签名块了,这里有具体的原因解释。

文档

上面这些文档是官方提供的,涵盖了从准备元信息、编写 yaml 描述文件,到构建与提交的全过程,建议先浏览一遍,有个整体流程的概念。

元信息准备

为了在 F-Droid 上正确显示应用的信息,需要准备一些元信息,包括:图标、截图、描述文件、ChangeLog 等,这些需要放在项目中,目录结构如下:

$ tree metadata/
metadata/
└──en-US
   ├──full_description.txt
   ├──short_description.txt
   ├──title.txt
   ├──changelogs
   │  └──2001.txt
   └──images
      ├──icon.png
      └──phoneScreenshots
         ├──1.png
         ├──2.png
         └──3.png
  • full_description.txt :应用的完整描述
  • short_description.txt :应用的简短描述
  • title.txt :应用的标题
  • changelogs/2001.txt :版本代码(versionCode)2001 的更新日志
  • images/icon.png :应用图标,512x512 像素
  • images/phoneScreenshots/ :应用截图,建议提供多张,分辨率无要求,但建议至少 1080x1920 像素

yaml 描述文件

F-Droid 使用 yaml 文件来描述应用的构建信息,文件名一般为包名加上 .yml 后缀,比如我的应用包名是 =net.liujiacai.flauth=,那么文件名就是 net.liujiacai.flauth.yml。这个文件需要放在 F-Droid 的数据仓库中,一般不放在应用代码仓库中。templates 目录下有一些模板,可以参考。 #31694 是我提交的 MR,历时一周合入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Categories:
  - Password & 2FA
  - Security
License: MIT
AuthorName: Jiacai Liu
AuthorEmail: [email protected]
WebSite: https://jiacai2050.github.io/flauth/
SourceCode: https://github.com/jiacai2050/flauth
IssueTracker: https://github.com/jiacai2050/flauth/issues

AutoName: Flauth

RepoType: git
Repo: https://github.com/jiacai2050/flauth
Binaries: https://github.com/jiacai2050/flauth/releases/download/v%v/flauth-android-arm64-v8a-v%v.apk

Builds:
  - versionName: 1.0.0
    versionCode: 2001
    commit: 979f69e489430da468776e61ecc7f16e53dad1fc
    sudo:
      - mkdir -p /home/runner/work/flauth
      - chown -R vagrant /home/runner/
    output: build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
    srclibs:
      - flutter@stable
    rm:
      - ios
      - linux
      - macos
      - windows
      - test
    prebuild:
      - flutterVersion=$(sed -n -E 's/.*flutter-version:[[:space:]]*"(.*)"/\1/p' .github/workflows/release.yml)
      - '[[ $flutterVersion ]]'
      - git -C $$flutter$$ checkout -f $flutterVersion
      - export repo=/home/runner/work/flauth/flauth
      - cd ..
      - mv net.liujiacai.flauth $repo
      - pushd $repo
      - export PUB_CACHE=$(pwd)/.pub-cache
      - $$flutter$$/bin/flutter config --no-analytics
      - $$flutter$$/bin/flutter packages pub get
      - popd
      - mv $repo net.liujiacai.flauth
    scandelete:
      - .pub-cache
    build:
      - export repo=/home/runner/work/flauth/flauth
      - cd ..
      - mv net.liujiacai.flauth $repo
      - pushd $repo
      - export PUB_CACHE=$(pwd)/.pub-cache
      - $$flutter$$/bin/flutter build apk --release --split-per-abi --target-platform="android-arm64"
      - popd
      - mv $repo net.liujiacai.flauth
    ndk: r28c

AllowedAPKSigningKeys: 6f089f1171a04315ff4905956c345e7fcb82a5232387e648c1c6e2af5344b22f

AutoUpdateMode: Version
UpdateCheckMode: Tags ^v[1-9].*
VercodeOperation:
  - '%c + 2000'
UpdateCheckData: pubspec.yaml|version:\s.+\+(\d+)|.|version:\s(.+)\+
CurrentVersion: 1.0.0
CurrentVersionCode: 2001
  1. Categories :分类,可以参考 官方文档 进行选择,我选择了 Password & 2FASecurity
  2. License :许可证类型,我选择了 MIT
  3. AuthorNameAuthorEmailWebSiteSourceCodeIssueTracker :这些都是应用的基本信息,按实际填写即可。
  4. AutoName :应用的名称。
  5. RepoTypeRepo :代码仓库类型和地址,我的应用托管在 GitHub 上,所以填写 git 和仓库地址。
  6. Binaries :预编译的二进制文件下载地址,我这里填写了 Android 版本的 apk 下载地址, %v 会被替换为版本号, %c 会被替换为版本号代码。这个需要配合后面的 AllowedAPKSigningKeys 一起使用,确保 apk 文件的签名正确。
  7. Builds :构建信息列表,每个版本对应一个条目,包含以下字段:

    • versionName :版本名称
    • versionCode :版本代码
    • commit :对应的 git 提交哈希
    • sudo :构建前需要执行的命令列表,这里我创建了工作目录并修改了权限。
    • output :构建产物路径
    • srclibs :需要使用的源代码库,这里我指定了 Flutter 的稳定版分支。
    • rm :构建前需要删除的目录列表,我删除了不需要的平台目录。
    • prebuild :构建前需要执行的命令列表,这里我设置了 Flutter 版本,获取依赖等。
    • scandelete :扫描后需要删除的目录列表,我删除了 .pub-cache 目录。
    • build :构建命令列表,这里我执行了 Flutter 的构建命令,需要注意的是,为了满足 reproducible build 的构建要求,这里对齐了 GitHub Actions 中的路径, 因为 Binaries 处指定的二进制就是从那里构建出来的。
    • ndk :指定使用的 NDK 版本,这里我选择了 r28c
  8. AllowedAPKSigningKeys :允许的 APK 签名密钥列表,我填写了我的签名公钥的 SHA-256 指纹。可以通过以下命令获取 apk 文件的签名指纹:

    1
    
    apksigner verify --print-certs flauth-android-arm64-v8a-v1.0.0.apk

    apksigner 工具包含在 Android SDK 的 build-tools 目录下,可以参考 官方文档 进行使用。macOS 上的路径是:

    1
    
    ~/Library/Android/sdk/build-tools/36.1.0/apksigner
  9. AutoUpdateMode :自动更新模式,我选择了 Version
  10. UpdateCheckMode :更新检查模式,我选择了基于标签的检查,正则表达式为 ^v[1-9].* ,表示匹配以 v 开头,后面跟数字的标签,比如 v1.0.0v2.1.3 等。
  11. VercodeOperation :版本代码操作,这里之所以需要这么做,是因为构建时使用了 split-per-abi ,会生成多个 apk 文件,好处是最终的 apk 体积变小。每个 apk 文件的版本代码需要不同,Flutter 会在 pubspec.yaml 中 versionCode 的基础上加 2000,比如是 1, 那么 arm64 架构就是 2001,其他架构可能会是 2002,根据实际调整就好,但 Android 手机一般现在都只需要 arm64 架构就够了。
  12. UpdateCheckData :更新检查数据,我指定了从 pubspec.yaml 文件中提取版本号和版本代码的正则表达式。
  13. CurrentVersionCurrentVersionCode :当前版本号和版本代码, fdroid checkupdates --allow-dirty com.example 命令会根据仓库中的最新版本自动更新这两个字段。

构建与提交

准备好上述文件之后,就可以开始构建与提交了。F-Droid 提供了一个 CI 环境,可以在其中进行构建和测试。具体的构建命令可以参考上面的 yaml 文件中的 prebuildbuild 部分。

1
2
3
4
5
6
git clone --depth=1 https://gitlab.com/fdroid/fdroidserver ~/fdroidserver
sudo sh -c 'apt-get update &&apt-get install -y docker.io'
sudo docker run --rm -itu vagrant --entrypoint /bin/bash \
  -v ~/fdroiddata:/build:z \
  -v ~/fdroidserver:/home/vagrant/fdroidserver:Z \
  registry.gitlab.com/fdroid/fdroidserver:buildserver

在容器里面,可以运行以下命令进行构建:

1
2
3
4
5
6
7
8
9
. /etc/profile
export PATH="$fdroidserver:$PATH" PYTHONPATH="$fdroidserver"
export JAVA_HOME=$(java -XshowSettings:properties -version 2>&1 > /dev/null | grep 'java.home' | awk -F'=' '{print $2}' | tr -d ' ')
cd /build
fdroid readmeta
fdroid rewritemeta net.liujiacai.flauth
fdroid checkupdates --allow-dirty net.liujiacai.flauth
fdroid lint net.liujiacai.flauth
fdroid build net.liujiacai.flauth

如果中途报错,可以根据日志进行修复,然后重新运行上面的命令即可。用 -v 参数可以看到更详细的日志。如果一切顺利,就可以给 F-Droid 提交 MR 了,这需要在 F-Droid 数据仓库 中创建一个 MR(在 gitlab 上),等待审核通过即可。

审核通过后,一般过一两天应用就会上线了,这时就可以通过 F-Droid 客户端进行安装和更新。

参考

评论

欢迎读者通过邮件与我交流,也可以在 MastodonTwitter 上关注我。