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

1,956 阅读2分钟

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 文件 image.png 可以看到类名是 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的模版项目和文件

image.png image.png

  • 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;
  }

创建成功

image.png