> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Flutter 打包发布
- URL: https://blog.vercanti.com/flutter-da-bao-fa-bu/
- Published: 2026-08-28T14:35:43.000Z
- Updated: 2026-08-28T14:59:28.000Z
- Description: 按提示填写密钥库密码、姓名、组织等信息。生成后妥善保管 .jks 文件，丢失无法恢复。 在 android/ 目录下创建 key.properties（不要提交到 Git）： 将 key.properties 加入 .gitignore： 编辑 android/app/build.gradle： flutter build appbundle --release 默认启用 R8 混淆。如果第三方库需要保留特定类，在 android/app/proguard-rules.pro 中添加规则： 在 build.gradle 中引用： 1. 在 Google
- Author: yellowdog
- Tags: 移动开发

> 官方文档：<https://docs.flutter.dev/deployment/android>  
> 适用版本：Flutter 3.x（2026-05-08 核实）

---

## Android 打包

### 生成签名 Keystore

```bash
keytool -genkey -v \
  -keystore ~/upload-keystore.jks \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -alias upload

```

按提示填写密钥库密码、姓名、组织等信息。生成后妥善保管 `.jks` 文件，丢失无法恢复。

### key.properties 配置

在 `android/` 目录下创建 `key.properties`（不要提交到 Git）：

```properties
storePassword=你的密钥库密码
keyPassword=你的密钥密码
keyAlias=upload
storeFile=/Users/yourname/upload-keystore.jks

```

将 `key.properties` 加入 `.gitignore`：

```
android/key.properties

```

### build.gradle 签名配置

编辑 `android/app/build.gradle`：

```groovy
// 在 android {} 块之前加载 key.properties
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    // ...
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
        }
    }
}

```

### 打包命令

| 命令                                          | 说明                    |
| ------------------------------------------- | --------------------- |
| flutter build apk --release                 | 生成单一 APK（所有 ABI 合并）   |
| flutter build apk --release --split-per-abi | 按 ABI 分包（推荐，体积更小）     |
| flutter build appbundle --release           | 生成 AAB，用于 Google Play |
| flutter build apk --debug                   | 构建 Debug 包            |
| flutter build apk --profile                 | 构建 Profile 包（性能分析）    |

### 构建命令参数

| 参数                        | 说明                                            |
| ------------------------- | --------------------------------------------- |
| \--release                | 发布模式，开启优化，禁用调试                                |
| \--debug                  | 调试模式，包含调试信息                                   |
| \--profile                | 性能分析模式                                        |
| \--split-per-abi          | 按 CPU 架构拆分 APK（arm64-v8a、armeabi-v7a、x86\_64） |
| \--obfuscate              | 混淆 Dart 代码（需配合 \--split-debug-info）           |
| \--split-debug-info=<dir> | 将调试符号输出到指定目录，用于还原崩溃堆栈                         |
| \--dart-define=KEY=VALUE  | 注入编译时常量                                       |
| \--target-platform        | 指定目标平台（android-arm、android-arm64、android-x64） |

```bash
# 完整发布示例
flutter build appbundle \
  --release \
  --obfuscate \
  --split-debug-info=build/debug-info

```

### 混淆（R8）配置

`flutter build appbundle --release` 默认启用 R8 混淆。如果第三方库需要保留特定类，在 `android/app/proguard-rules.pro` 中添加规则：

```pro
# 保留所有 Flutter 插件
-keep class io.flutter.** { *; }

# 保留特定第三方库（示例）
-keep class com.google.gson.** { *; }
-keepattributes Signature
-keepattributes *Annotation*

```

在 `build.gradle` 中引用：

```groovy
buildTypes {
    release {
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

```

### App Bundle（AAB）上传 Google Play

1. 在 Google Play Console 创建应用
2. 进入「发布」→「正式版」→「创建新版本」
3. 上传 `build/app/outputs/bundle/release/app-release.aab`
4. 填写版本说明，提交审核

---

## iOS 打包

### Apple Developer 账号配置

1. 在 [developer.apple.com](https://developer.apple.com) 注册 Apple Developer Program（年费 $99）
2. 在 Xcode 中登录账号：Xcode → Settings → Accounts → 添加 Apple ID
3. 在 Apple Developer 后台创建 App ID（Bundle Identifier 需与 `ios/Runner.xcodeproj` 中一致）

### Xcode 证书与 Provisioning Profile

1. 打开 `ios/Runner.xcworkspace`（必须用 `.xcworkspace`，不是 `.xcodeproj`）
2. 选择 Runner Target → Signing & Capabilities
3. 勾选 Automatically manage signing，选择对应 Team
4. Xcode 会自动创建证书和 Provisioning Profile

手动管理时需在 Apple Developer 后台创建：

- Distribution Certificate（App Store Distribution）
- App Store Provisioning Profile（关联 App ID 和证书）

### 打包命令

```bash
# 构建 IPA
flutter build ipa --release

# 指定 export options
flutter build ipa --release --export-options-plist=ExportOptions.plist

```

### ExportOptions.plist 配置

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key>
  <string>app-store</string>  <!-- app-store / ad-hoc / development / enterprise -->
  <key>teamID</key>
  <string>XXXXXXXXXX</string>  <!-- Apple Developer Team ID -->
  <key>uploadSymbols</key>
  <true/>
  <key>compileBitcode</key>
  <false/>
</dict>
</plist>

```

| 字段             | 可选值                                        | 说明                    |
| -------------- | ------------------------------------------ | --------------------- |
| method         | app-store, ad-hoc, development, enterprise | 分发方式                  |
| teamID         | Team ID 字符串                                | 苹果开发者团队 ID            |
| uploadSymbols  | true/false                                 | 是否上传符号表用于崩溃解析         |
| compileBitcode | true/false                                 | 新版 Xcode 已弃用，设为 false |

### App Store Connect 上传

方式一：Transporter（推荐命令行场景）

```bash
xcrun altool --upload-app \
  --type ios \
  --file build/ios/ipa/Runner.ipa \
  --username apple_id@example.com \
  --password app_specific_password

```

方式二：Xcode Organizer

1. Xcode → Window → Organizer
2. 选择对应 Archive → Distribute App
3. 选择 App Store Connect → 按向导完成上传

---

## 版本管理

### pubspec.yaml 版本号

```yaml
version: 1.2.3+45
#        ^^^^^  ^^
#        versionName  versionCode

```

- `versionName`（`1.2.3`）：显示给用户的版本号，对应 iOS `CFBundleShortVersionString` 和 Android `versionName`
- `versionCode`（`45`）：内部递增整数，对应 iOS `CFBundleVersion` 和 Android `versionCode`，每次发布必须递增

### 自动化版本号

在 CI/CD 中根据 Git tag 或构建号自动设置：

```bash
# 使用 Git tag 作为版本名，构建号使用 CI 环境变量
BUILD_NUMBER=${GITHUB_RUN_NUMBER:-1}
VERSION_NAME=$(git describe --tags --abbrev=0 2>/dev/null || echo "1.0.0")

flutter build appbundle \
  --build-name=$VERSION_NAME \
  --build-number=$BUILD_NUMBER

```

---

## 环境配置

### \--dart-define 传入环境变量

```bash
flutter run --dart-define=API_URL=https://api.example.com --dart-define=ENV=prod
flutter build apk --release --dart-define=API_URL=https://api.example.com

```

在 Dart 代码中读取：

```dart
const apiUrl = String.fromEnvironment('API_URL', defaultValue: 'http://localhost:3000');
const env = String.fromEnvironment('ENV', defaultValue: 'dev');

```

### \--dart-define-from-file（推荐用于多环境）

创建 `.env.prod.json`：

```json
{
  "API_URL": "https://api.example.com",
  "ENV": "prod",
  "APP_NAME": "MyApp"
}

```

```bash
flutter build apk --release --dart-define-from-file=.env.prod.json

```

将环境文件加入 `.gitignore`（含敏感信息时）：

```
.env.*.json

```

### flutter\_dotenv 方案

```yaml
# pubspec.yaml
dependencies:
  flutter_dotenv: ^5.1.0

```

```dart
// main.dart
await dotenv.load(fileName: '.env');

// 读取
final apiKey = dotenv.env['API_KEY'] ?? '';

```

`.env` 文件需要在 `pubspec.yaml` 中声明为资产：

```yaml
flutter:
  assets:
    - .env

```

---

## CI/CD

### GitHub Actions 自动打包（Android APK + iOS IPA）

```yaml
# .github/workflows/release.yml
name: Flutter Release Build

on:
  push:
    tags:
      - 'v*'

jobs:
  build-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.22.0'
          channel: 'stable'

      - name: 写入 key.properties
        run: |
          echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/key.properties
          echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
          echo "keyAlias=upload" >> android/key.properties
          echo "storeFile=/home/runner/upload-keystore.jks" >> android/key.properties

      - name: 解码 Keystore
        run: |
          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > /home/runner/upload-keystore.jks

      - run: flutter pub get

      - name: 构建 APK
        run: flutter build apk --release --split-per-abi

      - uses: actions/upload-artifact@v4
        with:
          name: android-apk
          path: build/app/outputs/flutter-apk/*.apk

  build-ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.22.0'
          channel: 'stable'

      - run: flutter pub get

      - name: 安装证书和 Profile
        uses: apple-actions/import-codesign-certs@v3
        with:
          p12-file-base64: ${{ secrets.P12_BASE64 }}
          p12-password: ${{ secrets.P12_PASSWORD }}

      - name: 构建 IPA
        run: flutter build ipa --release --export-options-plist=ExportOptions.plist

      - uses: actions/upload-artifact@v4
        with:
          name: ios-ipa
          path: build/ios/ipa/*.ipa

```

### GitHub Actions Secrets 配置

| Secret 名称        | 内容                                                 |
| ---------------- | -------------------------------------------------- |
| STORE\_PASSWORD  | Keystore 密码                                        |
| KEY\_PASSWORD    | Key 密码                                             |
| KEYSTORE\_BASE64 | Keystore 文件的 Base64 编码（base64 upload-keystore.jks） |
| P12\_BASE64      | iOS 证书 .p12 文件的 Base64 编码                          |
| P12\_PASSWORD    | iOS 证书密码                                           |

### Fastlane 自动化

```ruby
# fastlane/Fastfile

lane :android_release do
  gradle(
    task: 'bundle',
    build_type: 'Release',
    project_dir: 'android/',
  )
  upload_to_play_store(
    aab: 'build/app/outputs/bundle/release/app-release.aab',
    track: 'internal',
  )
end

lane :ios_release do
  build_app(
    workspace: 'ios/Runner.xcworkspace',
    scheme: 'Runner',
    export_method: 'app-store',
  )
  upload_to_app_store(
    skip_metadata: true,
    skip_screenshots: true,
  )
end

```

---

## 踩坑与注意事项

### iOS 证书过期

Apple 证书有效期为 1 年（Development）或 3 年（Distribution）。证书过期后已安装的 App 不受影响，但无法提交新构建。

- 定期检查证书有效期：Xcode → Settings → Accounts → 查看 Certificates
- CI/CD 中设置到期提醒（日历提醒或监控脚本）
- 证书更新后需要重新生成 Provisioning Profile 并更新 CI/CD 中的 Secrets

### Android minSdkVersion 兼容性

`minSdkVersion` 决定支持的最低 Android 版本。部分 Flutter 插件要求较高的 minSdkVersion：

```groovy
// android/app/build.gradle
defaultConfig {
    minSdkVersion 21  // Android 5.0，大多数插件的最低要求
    targetSdkVersion 34
}

```

升级 minSdkVersion 前检查各插件的要求，避免发布后因版本限制导致用户无法安装。

### 包体积优化

| 优化手段                             | 说明                                    | 参考收益      |
| -------------------------------- | ------------------------------------- | --------- |
| \--split-per-abi                 | 按 CPU 架构拆分 APK                        | 减少 30-50% |
| \--obfuscate                     | 混淆 Dart 代码                            | 减少 5-10%  |
| 图片压缩                             | 使用 TinyPNG 或 flutter\_image\_compress | 依图片数量而定   |
| 使用 WebP 格式                       | 替换 PNG/JPG                            | 减少 25-35% |
| 移除未使用的依赖                         | 定期清理 pubspec.yaml                     | 依情况而定     |
| flutter build apk --analyze-size | 分析包体积构成                               | 辅助诊断      |

### \--obfuscate 必须配合 --split-debug-info

单独使用 `--obfuscate` 而不指定 `--split-debug-info` 时，崩溃堆栈将完全不可读。必须保存生成的符号文件用于还原：

```bash
flutter build apk --release \
  --obfuscate \
  --split-debug-info=build/symbols  # 保存此目录

# 还原崩溃堆栈
flutter symbolize \
  --debug-info=build/symbols/app.android-arm64.symbols \
  --input=crash_stack.txt

```

### Android Keystore 丢失无法更新应用

上传到 Google Play 的 APK/AAB 签名后，如果 Keystore 文件丢失，将永远无法发布新版本更新（只能重新上架新应用）。

- 将 Keystore 备份到至少两个独立位置（云存储 + 本地加密备份）
- 推荐使用 Google Play App Signing（Google 托管 Keystore，上传密钥丢失仍可恢复）

---

## 最佳实践

**Keystore 单独管理，不提交到版本控制**：将 Keystore 文件、`key.properties` 加入 `.gitignore`，通过 CI 环境变量或 GitHub Actions Secrets 注入，避免签名密钥泄漏。

**使用 `flutter build appbundle` 而非 `apk` 上传 Play Store**：AAB 格式让 Google Play 按设备架构交付最小安装包，比 fat APK 小 30–50%，且 Play Store 要求新应用必须上传 AAB。

**每次发布前递增版本号并记录变更**：`pubspec.yaml` 的 `version: 1.2.3+45` 中 `1.2.3` 是用户可见版本，`+45` 是 versionCode（必须单调递增）。建立 CHANGELOG 记录每次发布内容，便于审核和回滚决策。

**iOS 发布通过 Xcode Organizer 验证后再提交**：使用 `flutter build ipa` 生成后，在 Xcode Organizer 中执行 Validate App，提前发现证书过期、entitlements 缺失等问题，避免 App Store Connect 审核被拒。

**CI/CD 流水线分离签名和构建步骤**：构建步骤无需签名（加速缓存），签名步骤只在发布分支触发，减少签名密钥在 CI 环境中的暴露时间。

---

## 常见陷阱

### 陷阱：`flutter build apk --release` 后安装到真机崩溃，Debug 正常

**现象：** Release 构建启动即崩溃，日志显示 `ClassNotFoundException` 或方法找不到。  
**原因：** Release 构建默认开启代码混淆（R8/ProGuard），某些反射调用的类名被混淆后找不到。  
**解决：** 在 `android/app/proguard-rules.pro` 中为相关类添加 `-keep` 规则；或临时用 `--no-obfuscate` 确认是混淆问题，再针对性添加规则。

### 陷阱：iOS 真机调试证书可用，但 App Store 构建报 `Provisioning profile doesn't match`

**现象：** Development 证书正常，Archive 时报 Provisioning Profile 不匹配。  
**原因：** App Store 发布需要 Distribution 证书 + App Store Provisioning Profile，与开发用的 Development 配置文件不同。  
**解决：** 在 Apple Developer 后台创建 App Store Distribution Profile，在 Xcode 的 Signing & Capabilities 中选择正确 Profile；使用 fastlane match 统一管理多环境证书。

### 陷阱：`versionCode` 未递增导致 Play Store 拒绝上传

**现象：** 上传 AAB 时 Play Console 报 `Version code 45 has already been used`。  
**原因：** `pubspec.yaml` 的 `+45` 与已发布版本相同，Play Store 要求每次上传的 `versionCode` 严格递增。  
**解决：** 每次发布前更新 `pubspec.yaml` 中的 build number；在 CI 中用 `flutter build appbundle --build-number=$CI_BUILD_NUMBER` 自动注入流水线编号，避免手动遗忘。

---

## 参见

[Flutter入门](https://blog.vercanti.com/flutter-ru-men/)  
[GoRouter完全指南](https://blog.vercanti.com/gorouter-wan-quan-zhi-nan/)  
[Flutter状态管理](https://blog.vercanti.com/flutter-zhuang-tai-guan-li/)