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 站 。

相关推荐
Carson带你学Android14 小时前
Android PC时代已到来?Chrome OS将和Android合并!
android·google·chrome os
牛蛙点点申请出战20 小时前
仿微信语音 WaveView -- Compose 实现
android·前端
没有了遇见2 天前
Android 基于JitPack Fork三方库代码 修改XPopup 资源ID异常BUG 并发布到仓库
android
sxczst2 天前
Launcher3 如何获取系统上的所有应用程序?
android
sxczst2 天前
如何在悬浮窗中使用 Compose?
android
XDMrWu2 天前
Compose 智能重组:编译器视角下的黑科技
android·kotlin
vivo高启强2 天前
R8 如何优化我们的代码(1) -- 减少类的加载
android·android studio
阿笑带你学前端2 天前
Flutter应用自动更新系统:生产环境的挑战与解决方案
前端·flutter
诺诺Okami2 天前
Android Framework-WMS-从setContentView开始
android
stringwu2 天前
惊爆!Flutter消息通道的超神全解析!
flutter