
最近在着手开发我的 《匠心星问》 ,它定位是一款 题库 应用,将集题目浏览、发布、解答、做题为一体。打算第一步先以 Flutter 为核心,准备题库资源。于是诞生《每日一题》 系列,准备精心设计一些 Flutter 的问题与解答,作为题库的养料。
加下来的几题,将从一个需求逐步演进,让答题者给出方案设计。本题的焦点是探讨:
列出 Flutter SDK 中所有 StatelesWidget 组件
1. 题目概述
请你设计一套方案,通过 dart 的命令行脚本,分析并提取 Flutter SDK 中所有的 StatelesWidget 的派生类。基础代码如下所示,命令行中传入 flutter_sdk 路径。题目考查:
- 对 Flutter SDK 文件目录的了解
- 对文件操作接口熟悉度
- 解析 Dart 类和整理数据的能力
dart
void main(List<String> args) async {
int length = args.length;
if (length < 1) {
print('run with <flutter_sdk_path>');
return;
}
final flutterSdkPath = args[0];
print('Parsing Flutter SDK at: $flutterSdkPath');
await parser(flutterSdkPath);
}
Future<void> parser(String sdkPath) async{
/// TODO 解析 sdk 中所有的 StatelessWidget 派生类
/// 得到组件名列表
}
2. 思路分析
实现,我们应该知道,Flutter SDK 中的组件都在 packages/flutter/lib/src
下。随便点击一个组件,进入源码,悬浮时就可以看到路径:

可以进入对应的文件夹中参观一下,如下所示。接下来就需要遍历其中的文件,收集期望的数据。

文件有了,但是如何解析 dart 文件是比较头疼的。如果单纯自己使用正则匹配,虽然可行,但是操作太复杂。Flutter 官方有一个 analyzer 的类库,可以分析 Dart 代码。包括在上下文中的类继承信息、字段、方法等,这对于代码

3.代码实现
第一步,遍历源码文件夹,找出所有的 dart 文件:
dart
String srcDir = p.join(sdkPath,'packages','flutter','lib','src');
final List<String> dartFiles = [];
await for (FileSystemEntity entity in Directory(sdkPath).list(recursive: true)) {
if (entity is File && entity.path.endsWith('.dart')) {
dartFiles.add(entity.path);
}
}
创建一个 RecursiveAstVisitor
访问器,在 visitClassDeclaration 回调中,可以访问类的声明信息。在其中可以得到其父类信息,校验名称是不是 StatelessWidget
即可:
dart
class StatelessWidgetVisitor extends RecursiveAstVisitor<void> {
final List<String> components = [];
@override
void visitClassDeclaration(ClassDeclaration node) {
final String className = node.name.lexeme;
final ExtendsClause? extendsClause = node.extendsClause;
if (extendsClause != null) {
final String superClass = extendsClause.superclass.name2.lexeme;
if (superClass == 'StatelessWidget') {
components.add(className);
}
}
super.visitClassDeclaration(node);
}
}
AnalysisContextCollection 是 分析上下文集合
,用于解析 Dart 代码, 其中 includedPaths 是要分析的目录。然后遍历每个 Dart 文件进行分析,ResolvedUnitResult
表示解析 AST 成功。
接下来创建 StatelessWidgetVisitor,在 accept 时会让访问者遍历 AST,从而触发 visitClassDeclaration
执行收集工作:
dart
final List<String> components = [];
final AnalysisContextCollection collection = AnalysisContextCollection(includedPaths: [srcDir]);
for (String file in dartFiles) {
try {
AnalysisContext context = collection.contextFor(file);
SomeResolvedUnitResult result = await context.currentSession.getResolvedUnit(file);
if (result is ResolvedUnitResult) {
StatelessWidgetVisitor visitor = StatelessWidgetVisitor();
result.unit.accept(visitor);
components.addAll(visitor.components);
}
} catch (e) {
// Skip files with errors
}
}
最后 components 列表就是收集的所有组件名,这里做了一个字母排序,并将其输出到了 stateless_components.md
中。这样就完成了第一版的 StatelesWidget 组件提取器:

dart
components.sort((a,b)=>a.compareTo(b));
File outfile = File('stateless_components.md');
await outfile.writeAsString(components.join("\n"));
print('Results saved to: ${outfile.path}');
虽然输出了结果,不过目前的解析器功能还非常薄弱,甚至连父类的父类是 StatelesWidget
的组件都解析不出来。接下来的题目将会逐步优化,以及收集更多的数据信息。通过解析可以全方位查看某个版本 Flutter SDK 组件的内容。
4. 小结
本题的核心,在于接触 如何用工程化的手段解析 SDK 并提取结构化信息。相比手工检索或靠阅读源码,本题所展示的方法借助 Dart 官方的 analyzer 工具链,实现了自动化分析和提取,非常适合做代码扫描、组件统计、依赖图谱等任务。
这正是《匠心星问》系列题库设计的初衷:不仅是"会不会用",而是鼓励开发者去思考 "怎么做才更合理、更系统" 。希望你在解决本题的过程中,收获的不只是 Flutter 技术本身,更是一种解决问题的方式。
后续的《每日一题》也将持续围绕实战问题展开,欢迎持续关注和参与,一起构建一个 开放、精致、有深度 的 Flutter 题库生态!
你是否也有想出的题目,欢迎投稿加入《匠心星问》共同打造!
如果你有其他的看法,或者有什么想要的题目、或者想提供题目和答案,都欢迎在评论区留言。更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。