You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart语言如何通过编程方式获取所有可导入的类及Dart文件

Dart 编程获取所有可导入文件及公开类实现方案

要实现这个需求不要用纯正则硬解析,Dart语法存在大量边界情况(注释中的类名、part文件、私有成员、语法嵌套),正则匹配的误判率极高,直接用官方维护的analyzer包做语法级解析是最稳妥的方案。

1. 添加依赖

在项目pubspec.yaml中添加开发依赖,版本号和你当前使用的Dart SDK版本适配即可:

dev_dependencies:
  analyzer: ^6.4.0
  path: ^1.8.0

2. 扫描所有可直接导入的Dart文件

可直接导入的Dart文件需要排除三类:缓存/构建输出目录下的文件、part of修饰的分片文件、你不需要的自动生成代码文件,参考实现:

import 'dart:io';
import 'package:path/path.dart' as p;

Future<List<File>> scanImportableDartFiles(String projectRoot) async {
  final root = Directory(projectRoot);
  final result = <File>[];

  await for (final entity in root.list(recursive: true, followLinks: false)) {
    // 跳过隐藏目录、构建缓存目录
    final pathSegs = p.split(entity.path);
    if (pathSegs.any((seg) => seg.startsWith('.') || seg == 'build' || seg == '.dart_tool')) {
      continue;
    }
    if (entity is! File || p.extension(entity.path) != '.dart') continue;

    // 排除part分片文件,这类文件不能被直接import
    final content = await entity.readAsString();
    if (RegExp(r'^\s*part\s+of\s+', multiLine: true).hasMatch(content)) continue;

    // 按需打开注释过滤生成文件
    // final fileName = p.basename(entity.path);
    // if (fileName.endsWith('.g.dart') || fileName.endsWith('.freezed.dart')) continue;

    result.add(entity);
  }
  return result;
}

3. 解析文件内可供外部导入的公开类

通过analyzer做语法树解析,筛选顶层、非下划线开头的类声明即可,这类声明在文件被import后可以直接被外部访问:

import 'dart:io';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:path/path.dart' as p;

Future<List<String>> parsePublicClasses(String dartFilePath) async {
  final absPath = p.normalize(p.absolute(dartFilePath));
  // 分析上下文可以批量复用,不需要每个文件新建一次
  final collection = AnalysisContextCollection(
    includedPaths: [absPath],
    sdkPath: p.dirname(p.dirname(Platform.resolvedExecutable)),
  );

  final session = collection.contextFor(absPath).currentSession;
  final resolvedUnit = await session.getResolvedUnit(absPath);
  final publicClasses = <String>[];

  for (final dec in resolvedUnit.unit.declarations) {
    // 筛选公开类:非下划线开头的顶层类声明
    if (dec is ClassDeclaration && !dec.name.lexeme.startsWith('_')) {
      publicClasses.add(dec.name.lexeme);
    }
    // 按需扩展:如果需要识别可导入的枚举、mixin、扩展方法,加对应类型判断即可
    // if (dec is EnumDeclaration && !dec.name.lexeme.startsWith('_')) {
    //   publicClasses.add(dec.name.lexeme);
    // }
  }
  return publicClasses;
}

4. 调用示例

Future<void> main() async {
  const projectPath = './';
  final files = await scanImportableDartFiles(projectPath);
  for (final file in files) {
    final relative = p.relative(file.path, from: projectPath);
    final classes = await parsePublicClasses(file.path);
    print('文件路径: $relative');
    print('公开可导入类: ${classes.join(', ')}\n');
  }
}

补充说明

  • 如果需要扫描pub依赖中的可导入文件和类,只需要把pub缓存对应的依赖路径加入扫描范围即可,解析逻辑完全一致
  • part分片文件不需要单独解析,它的公开成员会随主文件的导入自动对外暴露
  • 所有下划线开头的顶层成员都是库私有,即使文件被导入外部也无法访问,直接过滤即可

内容的提问来源于stack exchange,提问作者aguiadouro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 14:45:34