📦 第一章:多环境配置
为什么需要多环境?
在实际开发中,应用通常需要在多个环境下运行:开发环境(Development)用于日常开发调试,预发布环境(Staging)用于内部测试验证,生产环境(Production)用于正式用户。每个环境可能对应不同的 API 地址、第三方 SDK 密钥、应用图标甚至应用名称。
Flutter Flavors 概念
Flutter Flavors 类似于 Android 的 Build Variants 和 iOS 的 Schemes,允许你从同一套代码构建不同版本的应用。每个 Flavor 可以拥有独立的 应用 ID、应用名称、图标 和 配置参数。
使用 --dart-define 传递编译时变量
最简单的多环境方案是通过命令行参数 --dart-define 在编译时注入变量:
Shell # 开发环境构建 flutter run --dart-define=ENV=dev --dart-define=API_URL=https://dev.api.example.com # 生产环境构建 flutter run --dart-define=ENV=prod --dart-define=API_URL=https://api.example.com
使用 --dart-define-from-file
当变量较多时,可以将配置写入 JSON 文件,通过 --dart-define-from-file 一次性加载:
config/dev.json { "ENV": "dev", "API_URL": "https://dev.api.example.com", "ENABLE_LOGGING": "true", "APP_NAME": "MyApp Dev" }
Shell # 从 JSON 文件读取所有配置 flutter run --dart-define-from-file=config/dev.json
Android: build.gradle Flavor 配置
android/app/build.gradle // 在 android 块中配置 Flavors android { ... flavorDimensions "environment" productFlavors { dev { dimension "environment" applicationIdSuffix ".dev" resValue "string", "app_name", "MyApp Dev" } staging { dimension "environment" applicationIdSuffix ".staging" resValue "string", "app_name", "MyApp Staging" } prod { dimension "environment" resValue "string", "app_name", "MyApp" } } }
iOS: Xcode Schemes 配置
在 iOS 端,每个 Flavor 对应一个 Xcode Scheme。需要在 Xcode 中为 Runner 项目创建不同的 Scheme(如 dev、staging、prod),并为每个 Scheme 设置不同的 Bundle Identifier 和构建配置。
- 打开
ios/Runner.xcworkspace - 点击 Product → Scheme → Manage Schemes
- 为每个环境创建独立的 Scheme
- 在每个 Scheme 的 Build Configuration 中指定 Debug / Release
环境配置类
Dart // 环境配置类:在运行时读取编译时注入的变量 class EnvConfig { // 从 --dart-define 读取环境名称 static const String env = String.fromEnvironment( 'ENV', defaultValue: 'dev', ); // 读取 API 地址 static const String apiUrl = String.fromEnvironment( 'API_URL', defaultValue: 'https://dev.api.example.com', ); // 读取布尔值配置 static const bool enableLogging = bool.fromEnvironment( 'ENABLE_LOGGING', defaultValue: true, ); // 读取应用名称 static const String appName = String.fromEnvironment( 'APP_NAME', defaultValue: 'MyApp', ); // 判断是否为生产环境 static bool get isProduction => env == 'prod'; // 判断是否为开发环境 static bool get isDevelopment => env == 'dev'; } // 使用示例 void main() { print('当前环境: ${EnvConfig.env}'); print('API 地址: ${EnvConfig.apiUrl}'); if (EnvConfig.enableLogging) { // 仅在启用日志时初始化日志系统 setupLogger(); } runApp(const MyApp()); }
const 声明的 String.fromEnvironment 在编译时求值,这意味着未使用的环境分支代码会被 Tree-Shaking 移除,不会增加最终包体积。
🔨 第二章:构建与打包
Android 构建
构建 APK
Shell # Debug 模式构建(用于开发调试) flutter build apk --debug # Release 模式构建(用于发布) flutter build apk --release # Profile 模式构建(用于性能分析) flutter build apk --profile # 按 ABI 分割构建,减小包体积 flutter build apk --split-per-abi
构建 App Bundle(推荐)
Shell # Google Play 推荐使用 App Bundle 格式 flutter build appbundle --release # 输出路径: build/app/outputs/bundle/release/app-release.aab
签名配置
Shell # 第一步:生成密钥库文件 keytool -genkey -v \ -keystore ~/upload-keystore.jks \ -keyalg RSA -keysize 2048 -validity 10000 \ -alias upload
android/key.properties # 密钥库配置文件(不要提交到版本控制!) storePassword=你的密钥库密码 keyPassword=你的密钥密码 keyAlias=upload storeFile=/Users/yourname/upload-keystore.jks
android/app/build.gradle // 读取签名配置 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 file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } buildTypes { release { signingConfig signingConfigs.release // 启用代码缩减和混淆 minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
ProGuard / R8 配置
android/app/proguard-rules.pro # Flutter 相关规则 -keep class io.flutter.app.** { *; } -keep class io.flutter.plugin.** { *; } -keep class io.flutter.util.** { *; } -keep class io.flutter.view.** { *; } -keep class io.flutter.** { *; } -keep class io.flutter.plugins.** { *; } # 保留 Gson 序列化相关类(如使用了 Gson) -keepattributes Signature -keepattributes *Annotation*
版本号管理
pubspec.yaml # 格式:主版本.次版本.修订号+构建号 version: 1.2.3+45 # 1.2.3 → versionName(显示给用户) # 45 → versionCode / buildNumber(每次发布递增)
Shell # 也可以在构建时通过命令行覆盖版本号 flutter build apk --build-name=1.2.3 --build-number=45
iOS 构建
构建 IPA
Shell # 构建 iOS 发布包 flutter build ipa --release # 使用导出选项 flutter build ipa --export-options-plist=ios/ExportOptions.plist # 输出路径: build/ios/ipa/
构建前需要确保:
- 拥有有效的 Apple Developer 账号
- 配置了正确的 Provisioning Profile(开发/分发)
- 安装了对应的 签名证书(Development / Distribution)
- 在 Xcode 中设置了正确的 Bundle Identifier 和 Team
Web 构建
Shell # 构建 Web 版本 flutter build web --release # 使用 CanvasKit 渲染器(更好的一致性) flutter build web --web-renderer canvaskit # 使用 HTML 渲染器(更小的包体积) flutter build web --web-renderer html # 输出路径: build/web/ # 可部署到 Firebase Hosting、Nginx、GitHub Pages 等
桌面构建
Shell # macOS 构建 flutter build macos --release # Windows 构建 flutter build windows --release # Linux 构建 flutter build linux --release
代码混淆
Shell # 启用 Dart 代码混淆并分离调试信息 flutter build apk \ --obfuscate \ --split-debug-info=build/debug-info # iOS 同样支持 flutter build ipa \ --obfuscate \ --split-debug-info=build/debug-info # 保存 debug-info 目录!崩溃日志解析时需要用到 # 使用 flutter symbolize 还原混淆后的堆栈 flutter symbolize -i stack_trace.txt -d build/debug-info
.gitignore 中,切勿提交到版本控制系统。
🔄 第三章:CI/CD with GitHub Actions
工作流 YAML 结构
GitHub Actions 使用 YAML 文件定义自动化工作流。文件存放在 .github/workflows/ 目录下。每个工作流由触发条件(on)、作业(jobs)和步骤(steps)组成。
Flutter Setup Action
使用社区维护的 subosito/flutter-action 快速在 CI 环境中安装 Flutter SDK:
YAML # 安装指定版本的 Flutter - uses: subosito/flutter-action@v2 with: flutter-version: '3.24.0' channel: 'stable'
在 CI 中运行测试
YAML # 代码分析和测试步骤 - name: 代码分析 run: flutter analyze - name: 运行单元测试 run: flutter test --coverage - name: 检查代码格式 run: dart format --set-exit-if-changed .
缓存依赖
YAML # 缓存 pub 依赖以加快构建速度 - uses: actions/cache@v3 with: path: | ~/.pub-cache .dart_tool key: ${{ runner.os }}-pub-${{ hashFiles('pubspec.lock') }} restore-keys: | ${{ runner.os }}-pub-
完整工作流文件:测试 → 构建 → 部署
.github/workflows/flutter-ci.yml name: Flutter CI/CD # 触发条件 on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: # 第一个作业:测试 test: name: 测试与分析 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: '3.24.0' channel: 'stable' # 缓存依赖 - uses: actions/cache@v3 with: path: ~/.pub-cache key: ${{ runner.os }}-pub-${{ hashFiles('pubspec.lock') }} - name: 安装依赖 run: flutter pub get - name: 代码分析 run: flutter analyze - name: 代码格式检查 run: dart format --set-exit-if-changed . - name: 运行测试 run: flutter test --coverage # 上传测试覆盖率报告 - name: 上传覆盖率 uses: codecov/codecov-action@v3 with: file: coverage/lcov.info # 第二个作业:构建 Android build-android: name: 构建 Android needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: '3.24.0' # 设置 Java 环境 - uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' # 解码签名密钥(从 GitHub Secrets 中读取) - name: 解码密钥库 run: | echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore.jks echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties - name: 构建 APK run: flutter build apk --release --split-per-abi # 上传构建产物 - name: 上传 APK uses: actions/upload-artifact@v4 with: name: android-release path: build/app/outputs/flutter-apk/*.apk # 第三个作业:构建 iOS build-ios: name: 构建 iOS needs: test runs-on: macos-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: '3.24.0' - name: 构建 IPA run: flutter build ipa --release --no-codesign - name: 上传 IPA uses: actions/upload-artifact@v4 with: name: ios-release path: build/ios/ipa/*.ipa
${{ secrets.XXX }} 在工作流中引用,避免泄露。
🚀 第四章:Fastlane 自动化
安装 Fastlane
Shell # 通过 Homebrew 安装(macOS 推荐) brew install fastlane # 或通过 RubyGems 安装 gem install fastlane # 在项目的 android 或 ios 目录下初始化 cd android && fastlane init cd ios && fastlane init
Lanes 概念
Lane 是 Fastlane 中的核心概念,类似于任务流水线。每个 Lane 定义了一系列按顺序执行的操作(Action),如构建、测试、签名和上传。你可以为不同场景(如内测、正式发布)定义不同的 Lane。
Android Fastfile
android/fastlane/Fastfile # 定义 Android 构建和发布流程 default_platform(:android) platform :android do # 内部测试 Lane desc "构建并上传到 Google Play 内部测试" lane :internal do # 递增版本号 increment_version_code( gradle_file_path: "app/build.gradle" ) # 使用 Flutter 构建 App Bundle sh("cd ../.. && flutter build appbundle --release") # 上传到 Google Play 内部测试轨道 upload_to_play_store( track: "internal", aab: "../build/app/outputs/bundle/release/app-release.aab", json_key_file: "fastlane/play-store-key.json", skip_upload_metadata: true, skip_upload_screenshots: true ) end # 正式发布 Lane desc "构建并上传到 Google Play 生产轨道" lane :production do sh("cd ../.. && flutter build appbundle --release") upload_to_play_store( track: "production", aab: "../build/app/outputs/bundle/release/app-release.aab", json_key_file: "fastlane/play-store-key.json" ) end end
iOS Fastfile
ios/fastlane/Fastfile # 定义 iOS 构建和发布流程 default_platform(:ios) platform :ios do # TestFlight 内测 Lane desc "构建并上传到 TestFlight" lane :beta do # 使用 Match 管理证书和描述文件 match(type: "appstore") # 递增构建号 increment_build_number( xcodeproj: "Runner.xcodeproj" ) # 使用 Flutter 构建 IPA sh("cd ../.. && flutter build ipa --release") # 上传到 TestFlight upload_to_testflight( ipa: "../build/ios/ipa/Runner.ipa", skip_waiting_for_build_processing: true ) end # App Store 正式发布 Lane desc "构建并上传到 App Store" lane :release do match(type: "appstore") sh("cd ../.. && flutter build ipa --release") upload_to_app_store( ipa: "../build/ios/ipa/Runner.ipa", skip_metadata: false, skip_screenshots: false, submit_for_review: true, automatic_release: true ) end end
Match 证书管理
Match 是 Fastlane 的证书和描述文件管理工具。它将所有证书存储在一个私有 Git 仓库中,团队成员通过同步仓库来共享证书,避免手动管理的混乱。
Shell # 初始化 Match fastlane match init # 生成开发证书 fastlane match development # 生成分发证书 fastlane match appstore # 使用 Matchfile 配置 # ios/fastlane/Matchfile: # git_url("https://github.com/your-org/certificates") # storage_mode("git") # type("appstore") # app_identifier("com.example.myapp")
🏗️ 第五章:Codemagic & 其他 CI
Codemagic: Flutter 原生 CI/CD
Codemagic 是专为 Flutter 设计的 CI/CD 平台,提供开箱即用的 Flutter 构建环境,无需额外配置即可编译 Android、iOS、Web 和桌面应用。
- 支持可视化配置和 YAML 配置两种模式
- 内建 Apple 证书管理和 Code Signing
- 自动分发到 Google Play、App Store、Firebase App Distribution
- 提供 macOS 和 Linux 构建机器
codemagic.yaml 配置
codemagic.yaml workflows: flutter-app: name: Flutter App 构建 max_build_duration: 60 environment: flutter: stable xcode: latest cocoapods: default groups: - google_play_credentials - app_store_credentials triggering: events: - push branch_patterns: - pattern: 'main' include: true scripts: - name: 安装依赖 script: flutter pub get - name: 运行测试 script: flutter test - name: 构建 Android script: flutter build appbundle --release - name: 构建 iOS script: flutter build ipa --release artifacts: - build/**/outputs/**/*.aab - build/ios/ipa/*.ipa publishing: google_play: credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS track: internal app_store_connect: api_key: $APP_STORE_CONNECT_PRIVATE_KEY key_id: $APP_STORE_CONNECT_KEY_IDENTIFIER issuer_id: $APP_STORE_CONNECT_ISSUER_ID
GitLab CI for Flutter
.gitlab-ci.yml image: ghcr.io/cirruslabs/flutter:stable stages: - test - build test: stage: test script: - flutter pub get - flutter analyze - flutter test --coverage build_android: stage: build script: - flutter build apk --release artifacts: paths: - build/app/outputs/flutter-apk/ only: - main
CI/CD 平台对比
| 平台 | Flutter 支持 | iOS 构建 | 免费额度 | 特点 |
|---|---|---|---|---|
| GitHub Actions | 通过 Action 支持 | 支持(macOS Runner) | 2000 分钟/月 | 生态丰富、与 GitHub 深度集成 |
| Codemagic | 原生支持 | 内建支持 | 500 分钟/月 | Flutter 专用、配置简单 |
| GitLab CI | Docker 镜像 | 需自托管 Runner | 400 分钟/月 | 与 GitLab 仓库集成 |
| Bitrise | 内建 Step | 支持 | 有限 | 移动开发专用、可视化配置 |
| CircleCI | Orb 支持 | 支持 | 有限 | 高度可定制、并行任务 |
🤖 第六章:发布到 Google Play
Google Play Console 账户设置
- 访问 play.google.com/console 并注册开发者账号
- 支付一次性注册费 $25
- 完成账户身份验证
- 创建新应用并选择默认语言
应用商店列表信息
发布前需要准备以下素材:
- 应用名称:最多 30 个字符
- 简短描述:最多 80 个字符
- 完整描述:最多 4000 个字符
- 截图:至少 2 张,推荐手机 + 平板各提供
- 高分辨率图标:512 x 512px PNG
- 特征图:1024 x 500px
- 隐私政策 URL
发布轨道
Google Play 提供多个发布轨道,建议按以下顺序逐步推进:
1. 内部测试(Internal Testing)
最多 100 名测试人员,审核速度快(通常几分钟),适合团队内部快速验证。
2. 封闭测试(Closed Testing)
通过邮件列表或 Google Groups 邀请特定用户,适合小规模外部测试。
3. 开放测试(Open Testing)
任何用户都可以加入测试,适合大规模公开 Beta 测试。需要先通过审核。
4. 正式发布(Production)
面向所有用户发布。可以选择分阶段发布(如先推送给 10% 的用户)。
App Bundle 要求
自 2021 年 8 月起,新应用必须使用 App Bundle(.aab)格式提交。App Bundle 由 Google Play 动态生成针对各设备优化的 APK,通常可以减少 15%-20% 的下载体积。
发布步骤指南
- 确保应用已通过全部测试
- 在
pubspec.yaml中更新版本号 - 运行
flutter build appbundle --release - 登录 Google Play Console,进入你的应用
- 选择目标发布轨道(建议先从内部测试开始)
- 上传
.aab文件 - 填写发布说明(更新内容)
- 完成内容合规声明
- 提交审核
- 审核通过后,根据需要提升到更高轨道
应用内评价
Dart // 使用 in_app_review 包请求用户评价 // pubspec.yaml: in_app_review: ^2.0.0 import 'package:in_app_review/in_app_review.dart'; Future<void> requestReview() async { final inAppReview = InAppReview.instance; // 检查是否可以请求评价 if (await inAppReview.isAvailable()) { // 弹出系统原生评价弹窗 await inAppReview.requestReview(); } }
🍎 第七章:发布到 App Store
Apple Developer 账户
- 访问 developer.apple.com 注册
- 个人开发者年费 $99/年
- 企业开发者 $299/年(仅限企业内部分发)
- 注册后需等待 Apple 审核账户(通常 24-48 小时)
App Store Connect 设置
- 在 App Store Connect 中创建新 App
- 填写应用名称、Bundle ID、SKU
- 准备应用截图(iPhone 6.7"、6.5"、5.5",iPad 12.9")
- 编写应用描述、关键词、支持 URL
- 设置定价和可用区域
- 填写年龄分级问卷
TestFlight Beta 测试
TestFlight 是 Apple 官方的测试分发平台,可以在正式上架前邀请用户参与测试:
- 内部测试:最多 25 名 App Store Connect 用户,无需审核
- 外部测试:最多 10,000 名测试人员,需要 Beta App 审核(通常 24 小时内)
- 测试人员通过 TestFlight App 安装测试版本
- 每个 Build 的测试有效期为 90 天
App 审核要点
Apple 的应用审核非常严格,以下是需要特别注意的几个方面:
性能要求
- 应用不得崩溃或出现严重 Bug
- 不得包含未完成的 UI 或占位内容
- 必须在所有支持的设备上正常运行
隐私政策
- 必须提供隐私政策链接
- 正确声明数据收集类型
- 遵守 App Tracking Transparency
设计规范
- 遵循 Human Interface Guidelines
- 不得模仿系统 UI 误导用户
- 应用图标不得包含 Alpha 通道
内容规范
- 应用内购必须使用 Apple IAP
- 不得包含违法或有害内容
- 截图必须真实反映应用功能
常见被拒原因及应对
| 被拒原因 | 描述 | 解决方案 |
|---|---|---|
| Guideline 2.1 | 应用崩溃或存在 Bug | 充分测试后再提交 |
| Guideline 2.3 | 截图与实际功能不符 | 使用真实截图 |
| Guideline 3.1.1 | 未使用 Apple IAP 处理应用内购 | 数字内容必须使用 IAP |
| Guideline 4.0 | 应用功能过于简单 | 增加核心功能和价值 |
| Guideline 5.1.1 | 缺少隐私政策 | 添加完整的隐私政策页面 |
发布步骤指南
- 确保 Xcode 中配置了正确的 Bundle ID 和 Team
- 运行
flutter build ipa --release - 使用 Transporter 或
xcrun altool上传 IPA - 在 App Store Connect 中选择刚上传的 Build
- 填写发布说明
- 完成 App 审核信息(如登录凭据供审核员使用)
- 提交审核
- 审核通过后选择自动发布或手动发布
🌍 第八章:国际化实战
flutter_localizations 基础配置
pubspec.yaml dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter intl: ^0.19.0 # 启用代码生成 flutter: generate: true
l10n.yaml # 国际化代码生成配置文件 arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
ARB 文件
ARB(Application Resource Bundle)是 Flutter 推荐的本地化资源格式,本质上是 JSON 文件:
lib/l10n/app_en.arb { "@@locale": "en", "appTitle": "My App", "@appTitle": { "description": "应用标题" }, "hello": "Hello, {name}!", "@hello": { "description": "问候语", "placeholders": { "name": { "type": "String", "example": "World" } } }, "itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}", "@itemCount": { "description": "物品数量(复数形式)", "placeholders": { "count": { "type": "int" } } } }
lib/l10n/app_zh.arb { "@@locale": "zh", "appTitle": "我的应用", "hello": "你好,{name}!", "itemCount": "{count, plural, =0{没有物品} other{{count} 个物品}}" }
在 MaterialApp 中配置
Dart import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( // 应用标题 title: 'My App', // 本地化代理 localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], // 支持的语言列表 supportedLocales: const [ Locale('en'), // 英语 Locale('zh'), // 中文 Locale('ja'), // 日语 ], home: const HomePage(), ); } } // 在页面中使用本地化字符串 class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { // 获取本地化实例 final l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar(title: Text(l10n.appTitle)), body: Column( children: [ Text(l10n.hello('Flutter')), // "你好,Flutter!" Text(l10n.itemCount(5)), // "5 个物品" ], ), ); } }
easy_localization 方案
easy_localization 是一个流行的第三方国际化包,提供更灵活的 API 和更多功能:
Dart // pubspec.yaml: easy_localization: ^3.0.0 import 'package:easy_localization/easy_localization.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); runApp( EasyLocalization( // 支持的语言 supportedLocales: const [ Locale('en'), Locale('zh'), ], // 翻译文件路径 path: 'assets/translations', // 默认语言 fallbackLocale: const Locale('en'), child: const MyApp(), ), ); } // 使用方式: Text('app_title'.tr()) // 简单翻译 Text('hello'.tr(args: ['Flutter'])) // 带参数 Text('items'.plural(5)) // 复数形式 // 切换语言 context.setLocale(const Locale('zh'));
RTL 语言支持
对于阿拉伯语、希伯来语等从右到左(RTL)的语言,Flutter 会自动处理大部分布局翻转。但需要注意:
- 使用
Directionalitywidget 或依赖MaterialApp自动检测 - 使用
EdgeInsetsDirectional替代EdgeInsets - 使用
AlignmentDirectional替代Alignment - 图标和图片可能需要根据方向进行镜像
语言切换 UI
Dart // 语言切换下拉菜单示例 class LanguageSwitcher extends StatelessWidget { const LanguageSwitcher({super.key}); @override Widget build(BuildContext context) { return DropdownButton<Locale>( // 当前语言 value: context.locale, items: const [ DropdownMenuItem( value: Locale('zh'), child: Text('中文'), ), DropdownMenuItem( value: Locale('en'), child: Text('English'), ), DropdownMenuItem( value: Locale('ja'), child: Text('日本語'), ), ], // 切换语言回调 onChanged: (locale) { if (locale != null) { context.setLocale(locale); } }, ); } }
flutter gen-l10n 可以手动生成本地化代码。当 pubspec.yaml 中设置了 generate: true 时,flutter run 会自动生成。
🔥 第九章:热更新
Shorebird Code Push 概述
Shorebird 是目前 Flutter 生态中最成熟的热更新(OTA 代码推送)解决方案。它允许你在不经过应用商店审核的情况下,直接向用户设备推送 Dart 代码更新。
- 仅支持 Dart 代码更新,不支持原生代码和资源文件变更
- 支持 Android 和 iOS 平台
- 补丁体积通常只有几十 KB 到几百 KB
- 更新在下次应用启动时生效
安装与集成
Shell # 安装 Shorebird CLI curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash # 登录 shorebird login # 在项目中初始化 shorebird init # 这会在 pubspec.yaml 中添加 shorebird 配置 # 并在 shorebird.yaml 中生成应用 ID
创建发布版本和补丁
Shell # 第一步:创建基线发布版本 shorebird release android shorebird release ios # 第二步:修改代码后,创建补丁 shorebird patch android shorebird patch ios # 查看发布版本列表 shorebird releases list # 查看补丁列表 shorebird patches list
版本管理
Dart // 在应用中检查更新状态 import 'package:shorebird_code_push/shorebird_code_push.dart'; final shorebirdCodePush = ShorebirdCodePush(); // 检查是否有新补丁可用 final isUpdateAvailable = await shorebirdCodePush.isNewPatchAvailableForDownload(); if (isUpdateAvailable) { // 下载并安装补丁 await shorebirdCodePush.downloadUpdateIfAvailable(); // 提示用户重启应用以应用更新 } // 获取当前补丁版本号 final patchNumber = await shorebirdCodePush.currentPatchNumber();
限制
- 只能更新 Dart 代码,无法更新原生代码(Java/Kotlin/Swift/ObjC)
- 无法更新 资源文件(图片、字体等)
- 无法添加新的 原生插件
- iOS 平台需要符合 Apple 的相关政策
- 需要订阅 Shorebird 服务(有免费额度)
替代方案:服务端驱动 UI
另一种实现"动态更新"的方式是服务端驱动 UI(Server-Driven UI)。将 UI 结构以 JSON 等格式从服务器下发,客户端解析并渲染。这种方式不依赖热更新框架,但实现成本较高,且灵活性受限于预定义的组件库。
📊 第十章:监控与分析
Firebase Crashlytics 集成
Crashlytics 是 Firebase 提供的崩溃报告工具,可以实时收集和分析应用崩溃信息。
pubspec.yaml dependencies: firebase_core: ^3.0.0 firebase_crashlytics: ^4.0.0
Dart import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 捕获 Flutter 框架内的错误 FlutterError.onError = (errorDetails) { FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); }; // 捕获异步错误 PlatformDispatcher.instance.onError = (error, stack) { FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); return true; }; // 设置用户标识(可选,便于追踪特定用户的崩溃) FirebaseCrashlytics.instance.setUserIdentifier('user_123'); // 添加自定义日志 FirebaseCrashlytics.instance.log('应用启动'); // 设置自定义键值对 FirebaseCrashlytics.instance.setCustomKey('env', 'production'); runApp(const MyApp()); } // 手动上报非致命错误 try { // 可能出错的操作 await riskyOperation(); } catch (e, stackTrace) { // 记录非致命异常 await FirebaseCrashlytics.instance.recordError( e, stackTrace, reason: 'riskyOperation 执行失败', fatal: false, ); }
Sentry for Flutter
Sentry 是另一个广泛使用的错误监控平台,提供更丰富的错误上下文和性能监控:
Dart // pubspec.yaml: sentry_flutter: ^8.0.0 import 'package:sentry_flutter/sentry_flutter.dart'; Future<void> main() async { await SentryFlutter.init( (options) { options.dsn = 'https://your-dsn@sentry.io/project-id'; // 采样率:1.0 = 100% 的事件会被上报 options.tracesSampleRate = 1.0; // 环境标识 options.environment = 'production'; }, appRunner: () => runApp(const MyApp()), ); } // 手动捕获异常 try { doSomething(); } catch (exception, stackTrace) { await Sentry.captureException(exception, stackTrace: stackTrace); } // 添加面包屑(操作轨迹记录) Sentry.addBreadcrumb(Breadcrumb( message: '用户点击了购买按钮', category: 'ui.click', ));
Firebase Analytics 自定义事件
Dart // pubspec.yaml: firebase_analytics: ^11.0.0 import 'package:firebase_analytics/firebase_analytics.dart'; final analytics = FirebaseAnalytics.instance; // 记录自定义事件 await analytics.logEvent( name: 'purchase_completed', parameters: { 'item_id': 'SKU_12345', 'item_name': 'Flutter 课程', 'price': 99.0, 'currency': 'CNY', }, ); // 设置用户属性 await analytics.setUserProperty( name: 'membership_level', value: 'premium', ); // 记录页面浏览 await analytics.logScreenView( screenName: '商品详情页', screenClass: 'ProductDetailScreen', );
性能监控
Dart // pubspec.yaml: firebase_performance: ^0.10.0 import 'package:firebase_performance/firebase_performance.dart'; // 自定义性能追踪 final trace = FirebasePerformance.instance.newTrace('data_loading'); await trace.start(); // 执行需要监控的操作 final data = await fetchDataFromApi(); // 添加指标 trace.setMetric('data_size', data.length); trace.putAttribute('endpoint', '/api/products'); await trace.stop(); // HTTP 请求性能自动追踪 // firebase_performance 会自动监控 HTTP 请求的延迟和成功率
用户反馈收集
除了自动化的崩溃收集,主动收集用户反馈也很重要:
- 应用内反馈表单(截图 + 文字描述)
- 集成
shake摇一摇反馈(如 Instabug、Shake SDK) - 应用内评分请求(使用
in_app_review) - 将反馈数据发送到内部工单系统或 Slack/飞书通知
✏️ 第十一章:实践练习
练习 1:多环境 CI/CD 流水线
为一个 Flutter 项目搭建完整的 CI/CD 流水线:
- 创建
config/dev.json、config/staging.json、config/prod.json三个环境配置文件 - 编写
EnvConfig类读取编译时变量 - 编写 GitHub Actions 工作流:PR 时运行测试和分析,合并到 main 时自动构建 Release APK
- 在工作流中使用缓存加速依赖安装
- 将构建产物上传为 Artifacts
练习 2:完整国际化应用
为一个待办事项应用添加完整的国际化支持:
- 使用
flutter_localizations+intl配置中文和英文 - 编写 ARB 文件,包含简单文本、带参数文本和复数形式
- 实现语言切换功能,并使用
SharedPreferences持久化用户的语言选择 - 确保所有 UI 文本都使用本地化字符串,不存在硬编码文本
练习 3:应用监控与发布准备
为一个即将上线的应用完成发布前的工程化准备:
- 集成 Firebase Crashlytics,配置全局错误捕获
- 添加 3-5 个关键业务的自定义 Analytics 事件
- 配置代码混淆
--obfuscate并保存调试信息 - 编写 Fastlane 的
Fastfile,定义 beta 和 release 两个 Lane - 准备 Google Play 或 App Store 所需的所有素材清单(截图尺寸、描述、隐私政策等)