Flutter 百题斩#15 | 列出 SDK 所有 StatelesWidget 组件


最近在着手开发我的 《匠心星问》 ,它定位是一款 题库 应用,将集题目浏览、发布、解答、做题为一体。打算第一步先以 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 站 。

相关推荐
程序员JerrySUN39 分钟前
Linux 文件系统实现层详解:原理、结构与驱动衔接
android·linux·运维·数据库·redis·嵌入式硬件
2501_916013741 小时前
iOS 加固工具使用经验与 App 安全交付流程的实战分享
android·ios·小程序·https·uni-app·iphone·webview
南棱笑笑生2 小时前
20250715给荣品RD-RK3588开发板刷Android14时打开USB鼠标
android·计算机外设
hy.z_7773 小时前
【数据结构】反射、枚举 和 lambda表达式
android·java·数据结构
幻雨様3 小时前
UE5多人MOBA+GAS 20、添加眩晕
android·ue5
没有了遇见4 小时前
开源库 XPopup 资源 ID 异常修复:从发现 BUG 到本地 AAR 部署全流程
android
雮尘4 小时前
一文读懂 Android 屏幕适配:从基础到实践
android·前端
用户2018792831674 小时前
浅谈焦点冲突导致异常背景色的机制
android
2501_915106325 小时前
Fiddler 中文版抓包实战 构建标准化调试流程提升团队协作效率
android·ios·小程序·https·uni-app·iphone·webview
超龄超能程序猿5 小时前
(3)从零开发 Chrome 插件:网页图片的批量下载
android·java·javascript