Analyzer API Reference#
Source of truth:
analyzerpackage v10.0.1, used by dart_mappable_builder, json_serializable, and retrofit_generator. Import:package:analyzer/dart/element/element.dartandpackage:analyzer/dart/element/type.dart
Getting a Library#
In a Builder, the entry point is always the LibraryElement:
// Via BuildStep (most common)
final LibraryElement library = await buildStep.inputLibrary;
// Via Resolver (for other libraries)
final LibraryElement lib = await buildStep.resolver.libraryFor(
AssetId('other_pkg', 'lib/other.dart'),
);
// Via source_gen's LibraryReader (wraps LibraryElement)
final reader = LibraryReader(library);
Element Hierarchy#
Element
├── LibraryElement — a .dart file (or library directive)
├── ClassElement — class, abstract class, base class, interface, mixin, enum
│ ├── fields: List<FieldElement>
│ ├── methods: List<MethodElement>
│ ├── constructors: List<ConstructorElement>
│ ├── accessors: List<PropertyAccessorElement>
│ └── typeParameters: List<TypeParameterElement>
├── FieldElement — a field or variable inside a class
├── MethodElement — a method inside a class
├── ConstructorElement — a constructor
├── ParameterElement — a function/constructor parameter
│ ├── FieldFormalParameterElement — this.fieldName parameter
│ └── SuperFormalParameterElement — super.fieldName parameter
├── PropertyAccessorElement — getter or setter
├── TypeAliasElement — typedef
├── FunctionElement — top-level function
├── TopLevelVariableElement — top-level variable
└── TypeParameterElement — generic type parameter <T>
LibraryElement#
abstract class LibraryElement implements Element {
// All top-level elements: classes, functions, variables, typedefs
List<Element> get topLevelElements;
// Only declared class elements (does NOT include enums — use LibraryReader.enums for those)
List<ClassElement> get classes;
// Unit contains the AST for the library
CompilationUnit get unit;
// Package:uri identifier
String get identifier;
// Exported elements (what's visible to importers)
List<ExportElement> get libraryExports;
// All imported libraries
List<ImportElement> get libraryImports;
// Whether this is a part file (part of another library)
bool get isPart;
}
Iterating top-level elements:
final library = await buildStep.inputLibrary;
// All classes (including abstract, enums, mixins)
for (final element in library.topLevelElements.whereType<ClassElement>()) {
if (element.isEnum) { ... }
if (element.isMixin) { ... }
if (element.isAbstract) { ... }
}
ClassElement#
abstract class ClassElement implements Element {
// Identity
String get name; // 'MyClass'
String get displayName; // 'MyClass' or 'MyClass<T>'
// Members
List<FieldElement> get fields;
List<MethodElement> get methods;
List<ConstructorElement> get constructors;
List<PropertyAccessorElement> get accessors;
// Type parameters
List<TypeParameterElement> get typeParameters; // [T, U]
// Inheritance
InterfaceType? get supertype; // extends X
List<InterfaceType> get mixins; // with A, B
List<InterfaceType> get interfaces; // implements C, D
InterfaceType get thisType; // This class as an InterfaceType
// Classification
bool get isAbstract;
bool get isEnum;
bool get isMixin;
bool get isMixinClass;
bool get isSealed;
bool get isFinal; // final class
bool get isBase; // base class
bool get isInterface; // interface
// Find members
FieldElement? getField(String name);
MethodElement? getMethod(String name);
ConstructorElement? getNamedConstructor(String name);
PropertyAccessorElement? getGetter(String name);
PropertyAccessorElement? getSetter(String name);
// Annotations
List<ElementAnnotation> get metadata;
}
ClassElement: finding the default constructor#
// Get default (unnamed) constructor
final defaultCtor = element.constructors
.firstWhereOrNull((c) => c.isDefaultConstructor);
// Get named constructor
final fromJsonCtor = element.getNamedConstructor('fromJson');
// Get all non-factory constructors
final regularCtors = element.constructors.where((c) => !c.isFactory);
FieldElement#
abstract class FieldElement implements Element {
// Name and type
String? get name;
DartType get type;
// Access
PropertyAccessorElement? get getter;
PropertyAccessorElement? get setter;
// Modifiers
bool get isFinal;
bool get isConst;
bool get isStatic;
bool get isLate;
bool get isAbstract;
bool get isExternal;
bool get isSynthetic; // true for fields created by constructor initializers
// Default value (as source code string, e.g. "'hello'" or "42")
String? get defaultValueCode;
// Annotations
List<ElementAnnotation> get metadata;
}
Iterating class fields:
for (final field in classElement.fields) {
if (field.isStatic) continue; // Skip static fields
if (field.isSynthetic) continue; // Skip synthetic getters
if (!field.isPublic) continue; // Skip private fields
final typeName = field.type.getDisplayString(withNullability: true);
final isNullable = field.type.nullabilitySuffix == NullabilitySuffix.question;
}
MethodElement#
abstract class MethodElement implements Element {
String? get name;
DartType get returnType;
List<ParameterElement> get formalParameters;
List<TypeParameterElement> get typeParameters;
bool get isAbstract;
bool get isStatic;
bool get isExternal;
bool get isAsyncGenerator; // async*
bool get isGenerator; // sync*
List<ElementAnnotation> get metadata;
}
ParameterElement#
abstract class ParameterElement implements Element {
String? get name;
DartType get type;
// Parameter kind
bool get isRequiredPositional; // f(String x)
bool get isOptionalPositional; // f([String x])
bool get isRequiredNamed; // f({required String x})
bool get isOptionalNamed; // f({String? x})
bool get isNamed; // isRequiredNamed || isOptionalNamed
// Default value as source code string
String? get defaultValueCode; // e.g. "'hello'" or "42"
List<ElementAnnotation> get metadata;
}
// Specializations
abstract class FieldFormalParameterElement extends ParameterElement {
// this.fieldName in constructor
FieldElement? get field;
}
abstract class SuperFormalParameterElement extends ParameterElement {
// super.fieldName in constructor
ParameterElement? get superConstructorParameter;
}
ConstructorElement#
abstract class ConstructorElement implements Element {
String? get name; // null for default, 'fromJson' for named
bool get isDefaultConstructor; // true if name is null/empty
bool get isFactory;
bool get isConst;
bool get isAbstract;
List<ParameterElement> get formalParameters;
ClassElement get enclosingElement; // The class this constructor belongs to
// Redirect target (for factory redirecting constructors)
ConstructorElement? get redirectedConstructor;
}
TypeParameterElement#
abstract class TypeParameterElement implements Element {
String? get name; // 'T', 'K', etc.
DartType? get bound; // bound in 'T extends Comparable<T>'
}
ElementAnnotation#
// From element.metadata
abstract class ElementAnnotation {
// Compute the constant value (evaluate the annotation expression)
DartObject? computeConstantValue();
// The element (constructor or variable) being used as annotation
Element? get element;
// Check if it's a specific annotation
bool isFromPackage(String packageName);
}
Reading annotation values:
// With TypeChecker (preferred — handles inheritance)
import 'package:source_gen/source_gen.dart';
final checker = TypeChecker.fromRuntime(MyAnnotation);
// Get first annotation of a type on an element
final DartObject? obj = checker.firstAnnotationOf(element);
// Get all annotations of a type
for (final annotation in checker.annotationsOf(element)) {
// process annotation
}
// Read field values from DartObject
final stringVal = obj?.getField('name')?.toStringValue();
final intVal = obj?.getField('count')?.toIntValue();
final boolVal = obj?.getField('enabled')?.toBoolValue();
final doubleVal = obj?.getField('factor')?.toDoubleValue();
final listVal = obj?.getField('items')?.toListValue();
final mapVal = obj?.getField('mapping')?.toMapValue();
final typeVal = obj?.getField('type')?.toTypeValue();
// Enum field: access the enum string via revive
final enumAccessor = obj?.getField('myEnum')?.variable?.name;
DartType Hierarchy#
// package:analyzer/dart/element/type.dart
DartType
├── InterfaceType — class/interface/mixin/enum types
├── TypeParameterType — T, K, V (unresolved generic parameter)
├── FunctionType — Function(int) => String
├── RecordType — (int, String, {bool flag})
├── DynamicType — dynamic
├── VoidType — void
├── NeverType — Never
└── InvalidType — error in source code
DartType — Common Properties#
abstract class DartType {
Element? get element; // ClassElement for InterfaceType, etc.
// Nullability
NullabilitySuffix get nullabilitySuffix;
// NullabilitySuffix.question → T?
// NullabilitySuffix.none → T (non-nullable)
// NullabilitySuffix.star → T* (legacy/pre-NNBD)
// Quick checks
bool get isDynamic;
bool get isVoid;
bool get isBottom; // Never
// Get display string
// withNullability defaults to true and is deprecated as a parameter;
// use promoteToNonNull.getDisplayString() to strip '?'
String getDisplayString({@Deprecated('...') bool withNullability = true});
// getDisplayString() → 'List<String?>' (includes nullability)
// promoteToNonNull.getDisplayString() → 'List<String>' (strip nullability)
// Strip nullability
DartType get promoteToNonNull;
}
InterfaceType#
abstract class InterfaceType implements DartType {
ClassElement get element; // The class definition
// Generic type arguments: List<String> → [String]
List<DartType> get typeArguments;
// Inheritance
InterfaceType? get superclass;
List<InterfaceType> get interfaces;
List<InterfaceType> get mixins;
// Access class members
List<MethodElement> get methods;
List<PropertyAccessorElement> get accessors;
// Substitute type parameters
InterfaceType? asInstanceOf(ClassElement superclass);
// Gets how this type implements superclass with concrete type args
// e.g. List<String>.asInstanceOf(Iterable) → Iterable<String>
// Check if is a specific class
bool get isDartCoreList;
bool get isDartCoreMap;
bool get isDartCoreSet;
bool get isDartCoreIterable;
bool get isDartCoreString;
bool get isDartCoreInt;
bool get isDartCoreDouble;
bool get isDartCoreNum;
bool get isDartCoreBool;
bool get isDartCoreObject;
bool get isDartCoreNull;
bool get isDartAsyncFuture;
bool get isDartAsyncFutureOr;
bool get isDartAsyncStream;
}
TypeParameterType#
abstract class TypeParameterType implements DartType {
TypeParameterElement get element; // The type parameter definition
DartType get bound; // The declared bound (or Object if none)
}
FunctionType#
abstract class FunctionType implements DartType {
DartType get returnType;
List<ParameterElement> get parameters;
List<TypeParameterElement> get typeFormals; // Generic type params: <T>() => T
}
RecordType (Dart 3.0+)#
abstract class RecordType implements DartType {
List<RecordTypePositionalField> get positionalFields; // (int, String)
List<RecordTypeNamedField> get namedFields; // {bool flag}
}
abstract class RecordTypePositionalField {
DartType get type;
}
abstract class RecordTypeNamedField {
String get name;
DartType get type;
}
Nullability#
// Check if a type is nullable
final isNullable = type.nullabilitySuffix == NullabilitySuffix.question;
// Get the non-nullable version
final nonNullType = type.promoteToNonNull;
// Display with '?' (default — withNullability parameter is deprecated)
final display = type.getDisplayString(); // 'String?'
// Display without '?'
final displayNoNull = type.promoteToNonNull.getDisplayString(); // 'String'
InheritanceManager3#
Used to find inherited concrete members (from superclasses and mixins):
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
final manager = InheritanceManager3();
// Get all concrete members this class inherits.
// Accepts InterfaceElement (ClassElement is a subtype — both work).
final inheritedMap = manager.getInheritedConcreteMap(classElement);
// Key: Name (contains String name + optional library URI for private names)
// Value: ExecutableElement (MethodElement or PropertyAccessorElement/GetterElement)
for (final MapEntry(key: _, value: member) in inheritedMap.entries) {
if (member is GetterElement) {
final variable = member.variable;
if (variable is FieldElement && !variable.isStatic) {
// process inherited field
}
}
}
Note: InheritanceManager3 is in package:analyzer/src/ (internal API). It's used by json_serializable for
createSortedFieldSet().
Getting Fields Including Inherited#
json_serializable pattern for getting all fields including inherited ones:
// InheritanceManager3.getInheritedConcreteMap takes InterfaceElement;
// ClassElement is a subtype so it can be passed directly.
Map<String, FieldElement> _getAllFields(ClassElement element) {
// Direct instance fields
final ownFields = {
for (final f in element.fields)
if (!f.isStatic) f.name!: f,
};
// Inherited concrete fields via InheritanceManager3
final manager = InheritanceManager3();
final inheritedMap = manager.getInheritedConcreteMap(element);
final inheritedFields = <String, FieldElement>{};
for (final member in inheritedMap.values) {
if (member is GetterElement) {
final variable = member.variable;
if (variable is FieldElement) {
inheritedFields[variable.name!] = variable;
}
}
}
return {...inheritedFields, ...ownFields}; // own overrides inherited
}
Reading AST Nodes (Source-Level Analysis)#
For cases where constant evaluation isn't enough (e.g., reading complex annotation expressions), you can get the AST node:
// Via Resolver — astNodeFor takes a Fragment (use element.firstFragment)
final astNode = await buildStep.resolver.astNodeFor(
element.firstFragment,
resolve: true,
);
// The AstNode type depends on the element:
// ClassElement → ClassDeclaration (via element.firstFragment)
// FieldElement → VariableDeclaration (via element.firstFragment)
// MethodElement → MethodDeclaration (via element.firstFragment)
// ParameterElement → FormalParameter (via element.firstFragment)
// Cast to specific types
if (astNode is ClassDeclaration) {
final members = astNode.members;
final metadata = astNode.metadata;
}
Type Checking Utilities#
Used extensively to check type relationships:
import 'package:source_gen/source_gen.dart';
// Check if an element is of a specific type
final listChecker = TypeChecker.fromRuntime(List);
final isExactlyList = listChecker.isExactlyType(type); // is exactly List
final isAssignableToList = listChecker.isAssignableFromType(type); // is List or subtype
final isSuperOfList = listChecker.isSuperTypeOf(type); // is supertype of List
// SDK types need special handling
final intChecker = TypeChecker.typeNamed(int, inPackage: 'core', inSdk: true);
final futureChecker = TypeChecker.typeNamed(Future, inPackage: 'async', inSdk: true);
// Direct InterfaceType checks (no TypeChecker needed)
if (type is InterfaceType) {
type.isDartCoreList // type is List<...>
type.isDartCoreMap // type is Map<...>
type.isDartAsyncFuture // type is Future<...>
}
Traversing Type Arguments#
// Get the inner type of List<T>, Future<T>, etc.
DartType? getInnerType(DartType type) {
if (type is InterfaceType && type.typeArguments.isNotEmpty) {
return type.typeArguments.first;
}
return null;
}
// Get K and V from Map<K, V>
(DartType, DartType)? getMapTypes(DartType type) {
if (type is InterfaceType &&
type.isDartCoreMap &&
type.typeArguments.length == 2) {
return (type.typeArguments[0], type.typeArguments[1]);
}
return null;
}
// Get display string for a type with proper nullability
String displayType(DartType type) =>
type.getDisplayString(withNullability: true);
Resolving Inherited Generic Types#
When a subclass inherits from a generic superclass, use asInstanceOf to get concrete type arguments:
// class MyList extends List<String>
// Resolving type of inherited `length` getter:
final myListType = classElement.thisType;
final listType = myListType.asInstanceOf(listElement);
// listType is List<String> — the concrete parameterization
Common Imports#
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/constant/value.dart'; // DartObject
import 'package:analyzer/dart/ast/ast.dart'; // AstNode, ClassDeclaration, etc.
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; // InheritanceManager3