@@ -8,6 +8,40 @@ import 'package:analyzer/dart/element/type.dart';
88import 'package:build/build.dart' ;
99import 'package:glob/glob.dart' ;
1010
11+ const String kDefaultOutputPath = 'lib/msr_widgets.g.dart' ;
12+ const List <String > kDefaultScanDirectories = ['lib' ];
13+ const String kLibPrefix = 'lib/' ;
14+ const int kLibPrefixLength = 4 ; // Length of 'lib/'
15+
16+ const String _kSyntheticInputSuffix = r'lib/$lib$' ;
17+ const String _kGeneratedFilePattern = '.g.dart' ;
18+ const String _kDartFileGlob = '/**.dart' ;
19+
20+ const Set <String > _kExcludedWidgets = {
21+ 'Widget' ,
22+ 'StatelessWidget' ,
23+ 'StatefulWidget' ,
24+ };
25+
26+ const Set <String > _kFlutterBaseWidgetTypes = {
27+ 'Widget' ,
28+ 'StatelessWidget' ,
29+ 'StatefulWidget' ,
30+ 'InheritedWidget' ,
31+ 'RenderObjectWidget' ,
32+ 'ProxyWidget' ,
33+ };
34+
35+ const String _kGeneratedMapName = 'msrWidgetsForLayoutSnapshot' ;
36+ const String _kGeneratedFileHeader = '// GENERATED CODE - DO NOT MODIFY BY HAND' ;
37+ const String _kGeneratedFileIgnoreDirective = '// ignore_for_file: unused_import, implementation_imports' ;
38+
39+ const String _kPackageUriPrefix = 'package:' ;
40+ const String _kDartUriPrefix = 'dart:' ;
41+ const String _kFileUriPrefix = 'file:' ;
42+ const String _kLibPathSeparator = '/lib/' ;
43+ const String _kFlutterLibraryIdentifier = 'flutter' ;
44+
1145/// Finds all Flutter widgets used in a project and writes them
1246/// to a dart file as a map of widget name to class name.
1347///
@@ -53,48 +87,47 @@ class WidgetAnalyzerBuilder extends Builder {
5387
5488 WidgetAnalyzerBuilder ({
5589 required this .buildExtensions,
56- this .outputPath = 'lib/msr_widgets.g.dart' ,
57- this .scanDirectories = const [ 'lib' ] ,
90+ this .outputPath = kDefaultOutputPath ,
91+ this .scanDirectories = kDefaultScanDirectories ,
5892 });
5993
6094 @override
6195 FutureOr <void > build (BuildStep buildStep) async {
6296 // Only run on lib/$lib$ synthetic input
63- if (! buildStep.inputId.path.endsWith (r'lib/$lib$' )) {
97+ if (! buildStep.inputId.path.endsWith (_kSyntheticInputSuffix )) {
6498 return ;
6599 }
66100
67101 final allWidgets = < String , InterfaceElement > {};
68102
69103 // Scan all configured directories
70104 for (final directory in scanDirectories) {
71- final dartFiles = Glob ('$directory /**.dart ' );
105+ final dartFiles = Glob ('$directory $ _kDartFileGlob ' );
72106 await for (final input in buildStep.findAssets (dartFiles)) {
73- if (input.path.contains ('.g.dart' ) || input.path == outputPath) {
74- continue ;
75- }
76- try {
77- final resolver = buildStep.resolver;
78- if (! await resolver.isLibrary (input)) {
107+ if (input.path.contains (_kGeneratedFilePattern) || input.path == outputPath) {
79108 continue ;
80109 }
81- final library = await resolver.libraryFor (input);
82- for (final classElement in library.classes) {
83- if (_isWidgetClass (classElement)) {
84- final name = classElement.name;
85- if (name != null ) {
86- allWidgets[name] = classElement;
110+ try {
111+ final resolver = buildStep.resolver;
112+ if (! await resolver.isLibrary (input)) {
113+ continue ;
114+ }
115+ final library = await resolver.libraryFor (input);
116+ for (final classElement in library.classes) {
117+ if (_isWidgetClass (classElement)) {
118+ final name = classElement.name;
119+ if (name != null && ! _isPrivateWidget (name)) {
120+ allWidgets[name] = classElement;
121+ }
87122 }
88123 }
124+ await _scanLibraryForWidgets (library, allWidgets);
125+ } catch (e) {
126+ log.warning ('Error processing ${input .path }: $e ' );
89127 }
90- await _scanLibraryForWidgets (library, allWidgets);
91- } catch (e) {
92- log.warning ('Error processing ${input .path }: $e ' );
93128 }
94129 }
95- }
96- allWidgets
97- .removeWhere ((name, element) => name == 'Widget' || name == 'StatelessWidget' || name == 'StatefulWidget' );
130+ allWidgets.removeWhere ((name, element) => _kExcludedWidgets.contains (name));
98131 final outputId = AssetId (buildStep.inputId.package, outputPath);
99132 final dartCode = _generateDartFile (allWidgets, buildStep.inputId.package);
100133 await buildStep.writeAsString (outputId, dartCode);
@@ -155,7 +188,7 @@ class WidgetAnalyzerBuilder extends Builder {
155188 if (element is InterfaceElement ) {
156189 if (_checkExtendsWidgetRecursively (element, < InterfaceElement > {})) {
157190 final name = element.name;
158- if (name != null ) {
191+ if (name != null && ! _isPrivateWidget (name) ) {
159192 widgets[name] = element;
160193 }
161194 }
@@ -176,17 +209,17 @@ class WidgetAnalyzerBuilder extends Builder {
176209 for (final element in widgets.values) {
177210 final libraryUri = element.library.firstFragment.source.uri.toString ();
178211
179- if (libraryUri.startsWith ('package:' ) || libraryUri.startsWith ('dart:' )) {
212+ if (libraryUri.startsWith (_kPackageUriPrefix ) || libraryUri.startsWith (_kDartUriPrefix )) {
180213 imports.add ("import '$libraryUri ';" );
181- } else if (libraryUri.startsWith ('file:' ) && libraryUri.contains ('/lib/' )) {
182- final libPath = libraryUri.split ('/lib/' ).last;
183- imports.add ("import 'package: $packageName /$libPath ';" );
214+ } else if (libraryUri.startsWith (_kFileUriPrefix ) && libraryUri.contains (_kLibPathSeparator )) {
215+ final libPath = libraryUri.split (_kLibPathSeparator ).last;
216+ imports.add ("import '$ _kPackageUriPrefix $packageName /$libPath ';" );
184217 }
185218 }
186219
187220 // Write header comment
188- buffer.writeln ('// GENERATED CODE - DO NOT MODIFY BY HAND' );
189- buffer.writeln ('// ignore_for_file: unused_import, implementation_imports' );
221+ buffer.writeln (_kGeneratedFileHeader );
222+ buffer.writeln (_kGeneratedFileIgnoreDirective );
190223 buffer.writeln ();
191224
192225 // Write imports
@@ -197,7 +230,7 @@ class WidgetAnalyzerBuilder extends Builder {
197230 buffer.writeln ();
198231
199232 // Write the map
200- buffer.writeln ('const Map<Type, String> msrWidgetsForLayoutSnapshot = {' );
233+ buffer.writeln ('const Map<Type, String> $ _kGeneratedMapName = {' );
201234
202235 final sortedWidgets = widgets.keys.toList ()..sort ();
203236 for (final widgetName in sortedWidgets) {
@@ -217,7 +250,7 @@ class WidgetAnalyzerBuilder extends Builder {
217250 final libraryElement = element.library;
218251 final library = libraryElement.firstFragment.source.uri.toString ();
219252
220- if (element.name == 'Widget' && library.contains ('flutter' )) {
253+ if (element.name == 'Widget' && library.contains (_kFlutterLibraryIdentifier )) {
221254 return true ;
222255 }
223256
@@ -238,6 +271,10 @@ class WidgetAnalyzerBuilder extends Builder {
238271
239272 return false ;
240273 }
274+
275+ bool _isPrivateWidget (String name) {
276+ return name.startsWith ('_' );
277+ }
241278}
242279
243280/// AST visitor for method bodies to find widget instantiations
@@ -251,7 +288,7 @@ class _MethodBodyVisitor extends RecursiveAstVisitor<void> {
251288 final element = type.element;
252289 if (element is InterfaceElement ) {
253290 final name = element.name;
254- if (name != null ) {
291+ if (name != null && ! name. startsWith ( '_' ) ) {
255292 widgets[name] = element;
256293 }
257294 }
@@ -276,16 +313,11 @@ class _MethodBodyVisitor extends RecursiveAstVisitor<void> {
276313 final libraryElement = element.library;
277314 final library = libraryElement.firstFragment.source.uri.toString ();
278315
279- if (element.name == 'Widget' && library.contains ('flutter' )) {
316+ if (element.name == 'Widget' && library.contains (_kFlutterLibraryIdentifier )) {
280317 return true ;
281318 }
282319
283- if ((element.name == 'StatelessWidget' ||
284- element.name == 'StatefulWidget' ||
285- element.name == 'InheritedWidget' ||
286- element.name == 'RenderObjectWidget' ||
287- element.name == 'ProxyWidget' ) &&
288- library.contains ('flutter' )) {
320+ if (_kFlutterBaseWidgetTypes.contains (element.name) && library.contains (_kFlutterLibraryIdentifier)) {
289321 return true ;
290322 }
291323
0 commit comments