Skip to content
On this page

Flutter发布与部署

Flutter应用开发完成后,需要将其发布到各个应用商店或部署到目标环境中。本章将详细介绍Flutter应用的发布与部署流程,包括构建配置、应用商店发布、自动化部署等。

构建配置

构建模式

Flutter提供三种构建模式:

  1. Debug模式:用于开发和调试
  2. Profile模式:用于性能分析
  3. Release模式:用于发布
bash
# Debug模式(默认)
flutter run

# Profile模式
flutter run --profile

# Release模式
flutter run --release

构建配置文件

android/app/build.gradle中配置构建选项:

groovy
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // 应用ID
        applicationId "com.example.myapp"
        // 最低支持的Android版本
        minSdkVersion flutter.minSdkVersion
        // 目标Android版本
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        
        // 多Dex支持(如果应用超过65K方法)
        multiDexEnabled true
    }

    signingConfigs {
        release {
            // 从local.properties读取签名信息
            def keystoreProperties = new Properties()
            def keystorePropertiesFile = rootProject.file('key.properties')
            if (keystorePropertiesFile.exists()) {
                keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
            }

            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            // 启用代码混淆
            minifyEnabled true
            // 启用资源压缩
            shrinkResources true
            // 使用release签名配置
            signingConfig signingConfigs.release
            // 混淆规则文件
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    // 构建变体配置
    flavorDimensions "version"
    productFlavors {
        dev {
            dimension "version"
            applicationIdSuffix ".dev"
            versionNameSuffix "-dev"
        }
        prod {
            dimension "version"
            // 生产环境配置
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.multidex:multidex:2.0.1'
}

iOS构建配置

ios/Runner.xcodeproj/project.pbxproj中配置构建选项:

// 示例配置片段
/* Begin XCBuildConfiguration section */
		1B12E1A122C8AFE200321753 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_TEAM = "";
				ENABLE_BITCODE = NO;
				FLUTTER_BUILD_MODE = release;
				INFOPLIST_FILE = Runner/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				MTL_ENABLE_DEBUG_INFO = NO;
				PRODUCT_BUNDLE_IDENTIFIER = com.example.myapp;
				PRODUCT_NAME = $(TARGET_NAME);
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE";
				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};

应用签名

Android应用签名

  1. 创建密钥库:
bash
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
  1. android/key.properties中存储密钥信息:
storePassword=your-store-password
keyPassword=your-key-password
keyAlias=upload
storeFile=/home/user/upload-keystore.jks
  1. android/app/build.gradle中引用密钥信息(如上所示)

iOS应用签名

  1. 在Apple Developer网站创建证书和配置文件
  2. 使用Xcode自动管理签名或手动配置
  3. 配置Bundle Identifier和其他必要设置

构建APK/IPA

构建Android APK

bash
# 构建Release版APK
flutter build apk --release

# 构建分架构APK(减小APK大小)
flutter build apk --split-per-abi

# 构建App Bundle(推荐用于Google Play)
flutter build appbundle --release

构建iOS IPA

bash
# 在iOS目录下使用Xcode构建
cd ios
xcodebuild -workspace Runner.xcworkspace -scheme Runner archive

# 或使用Flutter命令(需要在macOS上)
flutter build ios --release

应用商店发布

Google Play发布

  1. 准备应用商店素材

    • 应用图标(512x512 PNG)
    • 屏幕截图(各种设备尺寸)
    • 应用描述和关键词
    • 分类和内容分级
  2. 创建发布清单

yaml
# fastlane/Fastfile
default_platform(:android)

platform :android do
  desc "Submit a new Beta Build to Crashlytics Beta"
  lane :beta do
    gradle(task: "clean assembleRelease")
    crashlytics
  end

  desc "Deploy a new version to the Google Play"
  lane :deploy do
    gradle(task: "clean assembleRelease")
    upload_to_play_store
  end
end
  1. 发布流程
bash
# 1. 构建App Bundle
flutter build appbundle --release

# 2. 构建分离APK(可选)
flutter build apk --split-per-abi --release

# 3. 使用bundletool验证
# bundletool build-apks --bundle=build/app/outputs/bundle/release/app-release.aab --output=my_app.apks

# 4. 上传到Google Play Console

Apple App Store发布

  1. 使用Transporter上传
bash
# 构建iOS应用
flutter build ios --release

# 在Xcode中归档并上传
# Product -> Archive -> Distribute App
  1. 使用命令行工具
bash
# 使用fastlane自动化发布
fastlane init
fastlane deliver --skip_binary_upload --skip_screenshots --skip_metadata

应用配置管理

环境配置

dart
// config/environment.dart
enum Environment { dev, staging, prod }

class AppConfig {
  static Environment _env = Environment.prod;

  static void setEnvironment(Environment env) {
    _env = env;
  }

  static String get baseUrl {
    switch (_env) {
      case Environment.dev:
        return 'https://api-dev.example.com';
      case Environment.staging:
        return 'https://api-staging.example.com';
      case Environment.prod:
        return 'https://api.example.com';
    }
  }

  static bool get enableLogging {
    return _env != Environment.prod;
  }

  static bool get enableDebugFeatures {
    return _env != Environment.prod;
  }
}

构建时配置

dart
// main_dev.dart
import 'config/environment.dart';

void main() {
  AppConfig.setEnvironment(Environment.dev);
  runApp(MyApp());
}

// main_prod.dart
import 'config/environment.dart';

void main() {
  AppConfig.setEnvironment(Environment.prod);
  runApp(MyApp());
}

构建命令:

bash
# 使用不同入口文件构建不同环境
flutter build apk --flavor dev --target lib/main_dev.dart
flutter build apk --flavor prod --target lib/main_prod.dart

自动化部署

使用GitHub Actions

yaml
# .github/workflows/deploy.yml
name: Deploy to App Stores

on:
  push:
    branches: [ main ]
  workflow_dispatch:

jobs:
  deploy-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '11'

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.x'
          channel: 'stable'

      - name: Get dependencies
        run: flutter pub get

      - name: Run tests
        run: flutter test

      - name: Build APK
        run: flutter build apk --release

      - name: Build App Bundle
        run: flutter build appbundle --release

      - name: Deploy to Google Play
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
          packageName: com.example.myapp
          releaseFiles: app/build/outputs/bundle/release/app-release.aab
          track: internal
          status: completed

  deploy-ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.x'
          channel: 'stable'

      - name: Get dependencies
        run: flutter pub get

      - name: Build iOS
        run: flutter build ios --release --no-codesign

      - name: Deploy to App Store Connect
        run: |
          xcrun altool --upload-app -f build/ios/ipa/Runner.ipa -u ${{ secrets.APPLE_ID }} -p ${{ secrets.APP_PASSWORD }}

使用Fastlane

ruby
# fastlane/lanes/deploy_android.rb
desc "Deploy Android app to Google Play"
lane :deploy_android do
  gradle(
    task: "clean",
    build_type: "Release"
  )
  
  # Build Flutter app
  sh("flutter", "build", "appbundle", "--release")
  
  # Upload to Google Play
  supply(
    track: "internal",
    aab: "build/app/outputs/bundle/release/app-release.aab",
    json_key: ENV["GOOGLE_PLAY_JSON_KEY"]
  )
end

# fastlane/lanes/deploy_ios.rb
desc "Deploy iOS app to App Store Connect"
lane :deploy_ios do
  # Build Flutter app
  sh("flutter", "build", "ios", "--release", "--no-codesign")
  
  # Build with xcodebuild
  build_app(
    workspace: "ios/Runner.xcworkspace",
    scheme: "Runner",
    export_method: "app-store"
  )
  
  # Upload to App Store Connect
  upload_to_app_store(
    skip_waiting_for_build_processing: true
  )
end

版本管理

版本号管理

yaml
# pubspec.yaml
name: my_app
description: A new Flutter project.

# 版本号格式:主版本号.次版本号.修订号
version: 1.0.0+1

environment:
  sdk: '>=2.19.0 <4.0.0'
  flutter: ">=3.0.0"

dependencies:
  flutter:
    sdk: flutter
  # ... 其他依赖

flutter:
  uses-material-design: true
  # ... 其他配置

自动化版本管理

bash
#!/bin/bash
# scripts/bump_version.sh

# 获取当前版本
current_version=$(grep 'version:' pubspec.yaml | cut -d ' ' -f 2 | cut -d '+' -f 1)
build_number=$(grep 'version:' pubspec.yaml | cut -d '+' -f 2)

# 解析版本号
IFS='.' read -ra VERSION_PARTS <<< "$current_version"
major=${VERSION_PARTS[0]}
minor=${VERSION_PARTS[1]}
patch=${VERSION_PARTS[2]}

case $1 in
    major)
        major=$((major + 1))
        minor=0
        patch=0
        ;;
    minor)
        minor=$((minor + 1))
        patch=0
        ;;
    patch)
        patch=$((patch + 1))
        ;;
    *)
        echo "Usage: $0 {major|minor|patch}"
        exit 1
        ;;
esac

new_version="$major.$minor.$patch"
new_build_number=$((build_number + 1))

# 更新pubspec.yaml
sed -i.bak "s/version: $current_version+$build_number/version: $new_version+$new_build_number/" pubspec.yaml

echo "Version bumped from $current_version+$build_number to $new_version+$new_build_number"

应用商店配置

Google Play Console配置

  1. 应用商店列表页面

    • 应用名称
    • 短描述
    • 长描述
    • 关键词
    • 应用类别
    • 内容分级
  2. 图形资产

    • 应用图标
    • 功能图像
    • 屏幕截图
    • 宣传视频(可选)
  3. 政策合规

    • 隐私政策URL
    • 应用内容说明
    • 数据安全表单

App Store Connect配置

  1. 应用信息

    • 应用名称
    • 副标题
    • 描述
    • 关键词
    • 支持URL
    • 营销URL
  2. 分类和评级

    • 主要类别
    • 次要类别
    • 年龄分级
  3. 隐私

    • 数据使用说明
    • 隐私政策URL

发布后监控

错误监控

dart
// services/error_reporting_service.dart
import 'package:firebase_crashlytics/firebase_crashlytics.dart';

class ErrorReportingService {
  static Future<void> init() async {
    // 启用Firebase Crashlytics
    await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
    
    // 设置错误处理
    FlutterError.onError = (FlutterErrorDetails details) {
      FirebaseCrashlytics.instance.recordFlutterError(details);
    };
    
    // 捕获未处理异常
    PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
      FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
      return true;
    };
  }

  static void recordError(dynamic error, StackTrace stackTrace) {
    FirebaseCrashlytics.instance.recordError(error, stackTrace);
  }

  static void recordNonFatalError(dynamic error, StackTrace stackTrace) {
    FirebaseCrashlytics.instance.recordError(error, stackTrace);
  }
}

性能监控

dart
// services/performance_monitoring_service.dart
import 'package:firebase_performance/firebase_performance.dart';

class PerformanceMonitoringService {
  static final _perf = FirebasePerformance.instance;

  static Future<void> startTrace(String traceName) async {
    final trace = _perf.newTrace(traceName);
    await trace.start();
  }

  static Future<void> stopTrace(String traceName) async {
    final trace = _perf.getTrace(traceName);
    await trace.stop();
  }

  static Future<String> observeHttpTracing(Uri uri) async {
    final httpMetric = _perf.newHttpMetric(uri, HttpMethod.Get);
    await httpMetric.start();
    
    // 执行HTTP请求
    final response = await http.get(uri);
    
    // 设置响应数据
    httpMetric.responseContentType = response.headers['content-type'];
    httpMetric.responsePayloadSize = response.body.length;
    httpMetric.httpResponseCode = response.statusCode;
    
    await httpMetric.stop();
    
    return response.body;
  }
}

热更新和动态化

使用CodePush(已停止服务,可考虑替代方案)

dart
// 现代替代方案:使用Flutter的动态功能或云函数
class DynamicFeatureService {
  static Future<void> checkForUpdates() async {
    // 实现动态更新检查逻辑
    // 可以结合Firebase Remote Config或其他后端服务
  }
}

使用Firebase Remote Config

dart
// services/remote_config_service.dart
import 'package:firebase_remote_config/firebase_remote_config.dart';

class RemoteConfigService {
  static final RemoteConfig _remoteConfig = RemoteConfig.instance;

  static Future<void> init() async {
    // 设置默认值
    await _remoteConfig.setDefaults(<String, dynamic>{
      'show_feature_x': true,
      'feature_x_threshold': 100,
      'welcome_message': 'Welcome!',
    });

    // 获取配置(有超时设置)
    await _remoteConfig.fetchAndActivate();
  }

  static bool getBool(String key) {
    return _remoteConfig.getBool(key);
  }

  static int getInt(String key) {
    return _remoteConfig.getInt(key);
  }

  static String getString(String key) {
    return _remoteConfig.getString(key);
  }
}

发布检查清单

发布前检查清单

  • [ ] 测试所有功能在Release模式下正常工作
  • [ ] 验证应用在不同设备和屏幕尺寸上的表现
  • [ ] 检查应用权限请求是否合理
  • [ ] 验证应用在弱网络环境下的表现
  • [ ] 确认应用符合各应用商店的政策要求
  • [ ] 检查应用包大小是否合理
  • [ ] 验证崩溃报告和错误监控正常工作
  • [ ] 确认隐私政策和用户协议已准备就绪
  • [ ] 准备好应用商店发布的所有素材
  • [ ] 验证付费功能和订阅设置(如果有)

性能验证

bash
# 验证APK大小
flutter build apk --release
du -h build/app/outputs/apk/release/app-release.apk

# 验证启动时间
adb shell am start -W -n com.example.myapp/.MainActivity

# 验证内存使用
adb shell dumpsys meminfo com.example.myapp

发布后维护

版本回滚计划

markdown
# 版本回滚计划

## 触发条件
- 关键功能出现严重bug
- 性能严重下降
- 大量用户投诉

## 回滚步骤
1. 立即停止新版本推广
2. 在应用商店暂停新版本分发
3. 必要时提交紧急修复版本
4. 通知用户暂时回退到旧版本
5. 分析问题原因并制定长期解决方案

用户反馈处理

dart
// services/feedback_service.dart
import 'package:firebase_analytics/firebase_analytics.dart';

class FeedbackService {
  static final analytics = FirebaseAnalytics.instance;

  static Future<void> logUserAction(String action, Map<String, dynamic>? params) async {
    await analytics.logEvent(name: action, parameters: params);
  }

  static Future<void> logScreenView(String screenName) async {
    await analytics.logScreenView(screenName: screenName);
  }

  static Future<void> setUserProperty(String name, String value) async {
    await analytics.setUserProperty(name: name, value: value);
  }
}

常见部署问题及解决方案

1. 构建失败

bash
# 检查Flutter环境
flutter doctor

# 清理构建缓存
flutter clean
flutter pub get

# 检查依赖冲突
flutter pub deps

2. 应用商店审核被拒

markdown
常见拒绝原因及解决方案:

1. 应用崩溃或功能异常
   - 解决:彻底测试所有功能

2. 隐私政策不合规
   - 解决:提供详细隐私政策

3. 应用描述与实际功能不符
   - 解决:确保描述准确反映功能

4. 未声明数据收集用途
   - 解决:在应用商店填写数据使用说明

3. 性能问题

dart
// 性能监控工具类
class PerformanceChecker {
  static void measureFunction(String name, Function fn) {
    final start = DateTime.now();
    fn();
    final end = DateTime.now();
    final duration = end.difference(start);
    
    if (duration.inMilliseconds > 16) {
      print('Performance warning: $name took ${duration.inMilliseconds}ms');
    }
  }
}

总结

Flutter应用的发布与部署是一个复杂但关键的过程,涉及多个环节:

  1. 构建配置:正确配置构建参数和签名
  2. 应用商店发布:遵循各平台的发布规范
  3. 自动化部署:使用CI/CD工具提高效率
  4. 版本管理:实施有效的版本控制策略
  5. 发布监控:持续监控应用性能和错误
  6. 维护更新:建立完善的后续维护机制

通过系统性的发布流程和持续的监控优化,可以确保Flutter应用在各个平台上稳定运行并提供良好的用户体验。