Flutter 项目品种

enum FlutterProjectType {
  app, // Flutter项目,主体是Flutter。
  module, // 主体是原生项目,用于在原生iOS、Android项目中增加Flutter模块,用于原生与Flutter混合开发。
  package, // 纯Flutter模块,不需求原生代码完成
  plugin, // 原生模块,在Flutter项目引进插件来完成原生的功用。
}

本文主要剖析 app 类型的创立过程,其他类型的原理类似

源码目录

翻开 flutter/packages/flutter_tools/lib/src/commands/create.dart 文件

通过源码分析Flutter项目创建过程
可以看到类名是 CreateCommand, runCommand() 函数是类的主函数

runCommand 主要流程

Future<FlutterCommandResult> runCommand() async {
    final FlutterProjectType template = _getProjectType(projectDir); // 项目类型, app/module....
    final List<String> platforms = stringsArg('platforms'); // 支撑系统渠道: ios/android/web...
    final String organization = await getOrganization(); // 项目组织称号
    final bool overwrite = boolArgDeprecated('overwrite'); // 是否掩盖已经存在的文件
    final String dartSdk = globals.cache.dartSdkBuild; // dart版本
    final bool includeIos; // 是否包括iOS项目
    final bool includeAndroid; // 是否包括Android项目
    final bool includeWeb; // 是否包括Web项目
    final bool includeLinux; // 是否包括Linux项目
    final bool includeMacos; // 是否包括MacOS项目
    final bool includeWindows; // 是否包括Windows项目
    if (template == FlutterProjectType.module) { // module 只支撑Android和iOS渠道
      includeIos = true;
      includeAndroid = true;
      includeWeb = false;
      includeLinux = false;
      includeMacos = false;
      includeWindows = false;
    } else {
      includeIos = featureFlags.isIOSEnabled && platforms.contains('ios');
      includeAndroid =
          featureFlags.isAndroidEnabled && platforms.contains('android');
      includeWeb = featureFlags.isWebEnabled && platforms.contains('web');
      includeLinux = featureFlags.isLinuxEnabled && platforms.contains('linux');
      includeMacos = featureFlags.isMacOSEnabled && platforms.contains('macos');
      includeWindows =
          featureFlags.isWindowsEnabled && platforms.contains('windows');
    }
    String? developmentTeam; // 获取iOS 的开发Team
    if (includeIos) {
      developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
        processManager: globals.processManager,
        platform: globals.platform,
        logger: globals.logger,
        config: globals.config,
        terminal: globals.terminal,
      );
    }
    // Flutter要求项目称号小写驼峰命名
    final String titleCaseProjectName = snakeCaseToTitleCase(projectName);
    // 项目信息Map
    final Map<String, Object?> templateContext = createTemplateContext(
      organization: organization, // 项目组织
      projectName: projectName, // 项目称号
      titleCaseProjectName: titleCaseProjectName,
      projectDescription: stringArgDeprecated('description'), // 项目描绘
      flutterRoot: flutterRoot, // flutter根目录
      androidLanguage: stringArgDeprecated('android-language'), // Android 运用的言语 Java/Kotlin
      iosLanguage: stringArgDeprecated('ios-language'), // iOS 运用的言语 OC/Swift
      iosDevelopmentTeam: developmentTeam, // iOS 开发team 称号
      ios: includeIos, // 是否包括iOS项目
      android: includeAndroid, // 是否包括Android项目
      web: includeWeb, // 是否包括 Web 项目
      linux: includeLinux, // 是否包括 Linux 项目
      macos: includeMacos, // 是否包括 MacOS项目
      windows: includeWindows, // 是否包括 Windows 项目
      dartSdkVersionBounds: "'>=$dartSdk <3.0.0'", // dart版本
      implementationTests: boolArgDeprecated('implementation-tests'),
      agpVersion: gradle.templateAndroidGradlePluginVersion,
      kotlinVersion: gradle.templateKotlinGradlePluginVersion, // kotlin版本
      gradleVersion: gradle.templateDefaultGradleVersion, // gradle版本
    );
    final bool creatingNewProject =
        !projectDir.existsSync() || projectDir.listSync().isEmpty;
    final Directory relativeDir = globals.fs.directory(projectDirPath);
    int generatedFileCount = 0;
    PubContext pubContext = PubContext.create;
    switch (template) {
      case FlutterProjectType.app:
        generatedFileCount += await generateApp( // generateApp办法去生成 App
          <String>['app', 'app_test_widget'],
          relativeDir,
          templateContext,
          overwrite: overwrite,
          printStatusWhenWriting: !creatingNewProject,
          projectType: template,
        );
        pubContext = PubContext.create;
        break;
      case FlutterProjectType.module:
        generatedFileCount += await _generateModule(
          relativeDir,
          templateContext,
          overwrite: overwrite,
          printStatusWhenWriting: !creatingNewProject,
        );
        pubContext = PubContext.create;
        break;
    }
    if (boolArgDeprecated('pub')) { // 更新Flutter pub依赖
      final FlutterProject project = FlutterProject.fromDirectory(relativeDir);
      await pub.get(
        context: pubContext,
        project: project,
        offline: boolArgDeprecated('offline'),
      );
    }
    return FlutterCommandResult.success();
  }

templates(模版)目录

flutter/packages/flutter_tools/templates 中存放各个projectType的模版项目和文件

通过源码分析Flutter项目创建过程
通过源码分析Flutter项目创建过程

  • android + android-java/android-kotlin
  • ios + ios-objc/ios-swift

generaeApp()

flutter/packages/flutter_tools/templates中复制项目和文件到目标directory

  Future<int> generateApp(
    List<String> templateNames,
    Directory directory,
    Map<String, Object?> templateContext, {
    bool overwrite = false,
    bool pluginExampleApp = false,
    bool printStatusWhenWriting = true,
    bool generateMetadata = true,
    FlutterProjectType? projectType,
  }) async {
    int generatedCount = 0;
    /// 复制 'app', 'app_test_widget', 'app_shared'三个目录文件
    generatedCount += await renderMerged(
      <String>[...templateNames, 'app_shared'], 
      directory,
      templateContext,
      overwrite: overwrite,
      printStatusWhenWriting: printStatusWhenWriting,
    );
    if (templateContext['android'] == true) { // 假如包括Android工程,则缓存Gradle
      generatedCount += _injectGradleWrapper(project);
    }
    if (androidPlatform) { // android 渠道需求额定更新属性
      gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
    }
    // 返回生成了多少个文件
    return generatedCount;
  }

创立成功

通过源码分析Flutter项目创建过程