flutter_graphql_codegen 3.0.0
flutter_graphql_codegen: ^3.0.0 copied to clipboard
A powerful code generator for Flutter GraphQL applications that generates type-safe Dart classes and client extensions from GraphQL schemas and operations.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
3.0.0 - 2026-10-21 #
Results follow the selections of each operation instead of the schema types.
See MIGRATION.md for upgrading; result_types: schema keeps
the 2.x output.
Breaking changes and migration #
- New option
result_types,operationby default: every selection gets its own class (GetUser_User,GetUser_User_Friends) with only the selected fields.parse<Op>Data,<op>Data(),read<Op>and the typed options return these classes. Setresult_types: schemato keep the 2.x classes - In operation mode,
types.darthas no classes for object types, interfaces and unions (and noUnknown<Name>fallbacks), andjson_serializableandtypes.g.dartare no longer used.json_annotationis still needed write<Op>takes the typed result instead of a raw map in operation mode:client.writeGetUser(vars, user). Withresult_types: schemait still takes the map- Input classes are immutable in operation mode:
constconstructor andfinalfields. UsecopyWithinstead of assigning fields - With
unknown_enum_fallback,$unknowninside an enum list of an input now throwsArgumentErrorintoJson()(operation mode), like a single value - Generated code needs Dart language 3.0 or newer (sealed classes, patterns)
- Removed the helpers deprecated in 2.1:
GraphQLCodeGenerator.generateCode(usegenerateOperationFile);OperationAnalyzer.getOperationReturnType,getOperationFieldName,hasSkipDirectiveandgetSkippableFields;SchemaAnalyzer.extractCustomScalars,getSchemaType,mapSchemaTypeToDartType,isScalarTypeandmapGraphQLTypeToDartType(closest:getDartType);TypeGenerator.generateClassForType,generateTypeDefinitions(usegenerateTypesFile) andgetDartTypeFromOriginal
Added #
- Selecting
user { id }works even whenUser.nameis non-null in the schema. Aliases become separate fields, fields selected more than once are merged, and fields reached only through@skip/@includeare nullable.strict_nullabilityworks as before - An operation with several root fields returns
<Op>Result; with one root field, that field's class - Interfaces and unions become sealed classes with an
<Base>_On<Type>subclass per type with its own fields and<Base>_Otherfor every other type, including types added to the server later, so aswitchover them is exhaustive - Every result class has a
constconstructor,finalfields,fromJson,toJson(response shape,__typenameincluded),==/hashCodethat compare lists and maps by elements, and a typedcopyWiththat can set nullable fields tonull. Classes other than the root havetypename - Fragments become
abstract interface classes in the newfragments.dart(exported fromindex.dart), implemented by every class that spreads the fragment unconditionally, with interfaces for nested selections and<Fragment>_On<Type>for fragments on interfaces and unions. For each fragment there is also a<Fragment>Dataclass, a<fragment>Fragmentdocument and a<fragment>FragmentNameconstant read<Fragment>Fragmentandwrite<Fragment>FragmentonGraphQLClientread and write one object's fragment in the cache- Input classes have
==/hashCode(deep for lists) and a typedcopyWiththat can set fields tonull - With
unknown_enum_fallback, enum fields of result classes and fragment interfaces get a<field>Rawsibling;toJson()writes it back for$unknown - One
build_runner buildis enough on a fresh checkout (operation mode) BLD108: two operations, or an operation andfragments.dart, that would declare the same top-level name fail with both names in the message instead of producing anindex.dartthat doesn't compile
Changed #
types.dartdeclaresgraphqlDeepEqualsandgraphqlDeepHash, also withresult_types: schema- Fragment names must be unique across all documents in operation mode:
every document reads every fragment for
fragments.dart. A fragment named like an enum, input or scalar declaration oftypes.dartgets a trailing underscore. Two fragments that would declare the same class fail withBLD102 - Aliases starting with
__are reserved and reported asBLD107. An alias__typenameon another field is reported even withvalidate_documents: false, since the generated code couldn't compile - Fragments on introspection types (
fragment FullType on __Type) are left out offragments.dart; operations using them keep returning raw maps
Known limitations #
- With
unknown_enum_fallback, an operation whose single root field is an enum can't be written to the cache while that field is$unknown
2.5.0 - 2026-10-21 #
Added #
include_null_inputs: send a null input field or optional variable asnullinstead of leaving it out, so the server can tell "clear it" from "leave it". Variables with a default value are still left out when null- With
unknown_enum_fallback, enum fields of object types get a<field>Rawsibling with the string the server sent, so a value that parsed to$unknowncan still be shown or logged evict<Operation>on the client extension of queries removes the cached result for given variables, so the nextcacheFirstread fetches it again
Fixed #
- An operation named like a
GraphQLClientmember (Query,Mutate,Subscribe,Link,Fragment, ...) generated extension methods that the client's own members shadowed, and code that didn't compile. Those methods now get a trailing underscore (client.query_(),client.readQuery_()) - A schema without object or input types produced a
types.dartwithpart 'types.g.dart', which json_serializable never writes - A schema type named
Enumclashed with the built-inEnumConverter; it is nowEnum_, like otherdart:corenames - Empty types (
type Mutation {}), unions and enums, and an interface listed twice inimplementsproduced code that didn't compile. The build now fails withSCH402, listing every such type - Operations selecting the introspection fields
__schemaor__typefailed withBLD103. Their result is now the raw JSON (Map<String, dynamic>?), since those types aren't part of the SDL
2.4.1 - 2026-10-21 #
Fixed #
- Didn't resolve next to
build3, 4 or 5 (currentbuild_runner,source_gen4,hive_ce_generator):buildwas limited to 2.x. Now>=2.4.0 <6.0.0 - Didn't resolve next to
hive_generatoron current Dart (3.9): it keepsdart_styleat 2.3.6 (newer 2.x versions need ananalyzerthat no longer installs), and 2.2.0โ2.4.0 required 2.3.7.dart_style2.3.0 and newer work again; versions before 2.3.7 format without a language version
2.4.0 - 2026-10-21 #
Changed #
- A
nullin a nullableDateTime,LocalDate,Decimal,Long,Byteor custom scalar field (or list element) now staysnull. It used to becomeDateTime.now(),Decimal('0'),Byte(0)or a wrapper aroundnull. Non-nullable fields behave as before - A field named like a type now gets a trailing underscore (
Media_), also when that type isn't used in the class
Fixed #
- Fields, variables and enum values starting with an underscore (
_id) generated private named parameters that don't compile; they are nowid_ - A field named like a type (
int: Int,String: String,Media: Media) shadowed the type and didn't compile - Lists of custom or built-in scalars with nullable elements (
[Custom],[DateTime]) and nested lists ([[DateTime]]) didn't compile
2.3.0 - 2026-10-17 #
Added #
- Typed options for
package:graphql: every operation file has<operation>QueryOptions/<operation>WatchQueryOptions(queries),<operation>MutationOptionsand<operation>SubscriptionOptions. They take typed variables and the full set of options (fetch and error policies, polling, optimistic results, mutationupdate, ...), and use the generated parser asparserFn, soparsedDataandObservableQueryresults are typed read<Operation>/write<Operation>cache helpers on the client extension of queriesscalar_mappingentries can be{type, import, converter}: the import is added to the generated files, and the converter is used for fields, list elements, variables and results. Mapped types without a converter are read withfromJsonand written withtoJsonstrict_scalars: fail withSCH401for custom scalars without a mappingdart run flutter_graphql_codegen:fetch_schemadownloads the schema fromschema_endpoint(new config key) intoschema_url, so the schema can live in the repository and builds don't need the network
Fixed #
- Variables of a scalar mapped to
DateTime(scalar_mapping: {Date: DateTime}) were put into the request asDateTimeobjects, which can't be sent as JSON. They are now serialized likeDateTimescalars
2.2.0 - 2026-10-17 #
Added #
- Generated files are formatted with
dart formatfor the language version of your package, so format checks in CI pass without excluding them types.dartexportspossibleTypesforGraphQLCache(possibleTypes: possibleTypes), so fragments on interfaces and unions are matched when reading from the cache- Operations are checked against the schema before generation: unknown fields
at any depth (
BLD103, previously root fields only), unknown or missing required arguments (BLD105), undeclared, unused or wrongly typed variables (BLD106) and missing or unexpected selection sets and invalid fragment types (BLD107). All problems are reported together with path and line.validate_documents: falseturns the checks off
Fixed #
- Schema types named like identifiers the generated code uses (
Map,String,Object,Future,Set,JsonKey, object types namedDecimal/Long/Byte, ...) produced code that didn't compile. Their Dart names now get a trailing underscore (type Map->class Map_) - With
strict_nullability, non-null lists of custom scalars,DateTime,LocalDateandDecimal([JSON!]!) didn't compile: the list helpers returned nullable lists
Changed #
- A build now fails for operations the schema rejects (they used to generate
code that failed at runtime). Set
validate_documents: falseto keep the old behavior while fixing them - Requires Dart 3.4 or newer (Flutter 3.22+), because
dart_styleneeds it; projects on older SDKs keep resolving 2.1.0 - Requires
build2.4.0 or newer; addsdart_styleandpub_semverdependencies
2.1.0 - 2026-10-17 #
Added #
- Schema descriptions become doc comments on generated classes, fields, enums
and enum values;
@deprecatedis mentioned there asDeprecated: <reason> deprecated_annotationsoption: emit Dart@Deprecatedfor schema@deprecatedfields and enum valuesunknown_enum_fallbackoption: enums get a$unknownvalue that unknown server values parse to (including in lists) instead of throwing- Error code
BLD104for duplicate operation names, in one document or across documents, and for names that map to the same output file; generated operation files record their source document in a// Source:line - Unknown keys in
graphql_codegen.yamlare reported as warnings, with a suggestion for likely typos
Fixed #
- Lists of enums with escaped values (e.g.
[Role!]containingdefault) failed to parse:json_serializabledidn't know the GraphQL names. Escaped values now carry@JsonValue - The usage hint in
index.dartshowed an invalidpackage:your_app/lib/...import and a made-up operation; it now shows the realpackage:URI
Deprecated #
- Public helpers the generator no longer uses, to be removed in 3.0:
TypeGenerator.generateClassForType,generateTypeDefinitionsandgetDartTypeFromOriginal;OperationAnalyzer.getOperationReturnType,getOperationFieldName,hasSkipDirectiveandgetSkippableFields;SchemaAnalyzer.extractCustomScalars,getSchemaType,mapSchemaTypeToDartType,isScalarTypeandmapGraphQLTypeToDartType;GraphQLCodeGenerator.generateCode(usegenerateOperationFile)
2.0.0 - 2026-10-10 #
Breaking changes and migration #
- Typed
*Datamethods throwOperationExceptionon GraphQL or network errors instead of returningnull. Wrap calls intry/on OperationExceptionif you relied onnull. - An operation that selects several root fields now returns an
<Operation>Resultobject instead of only the first field. - Custom scalar wrapper classes store the received JSON in
raw;valueis still aString.MyScalar('text')keeps working. DateTimeConverter.toJsonandLocalDateConverter.toJsonreturndynamic.- Unknown enum values throw
ArgumentErrorinstead ofException. - The placeholder
Awesomeclass was removed. - Generated object types that implement interfaces or belong to unions now
declare
implements ....
Added #
- Config options
strict_nullability,file_naming,emit_index,diagnostics_formatandscalar_mapping - Structured error codes (
CFG*,SCH*,BLD*) with plain or JSON output - Deterministic output: sorted types, fields and enum values, files written only when their content changes
- Operations are parsed with the
gqlAST parser instead of regular expressions - Interfaces and unions: generated as abstract classes whose
fromJsondispatches on__typename, withUnknown<Name>fallbacks; object typesimplementstheir interfaces and unions - Typed
<Operation>Variablesclasses withtoJson()(enums, dates, custom scalars and input types are serialized; unset optional variables are omitted) - Public
<operation>Document/<operation>OperationNameconstants andparse<Operation>Datafunctions, usable withwatchQuery, custom fetch policies, etc. - Operations with several root fields return a
<Operation>Resultclass - Fragments defined in other
.graphqlfiles (matched bydocument_paths) __typenameis added to every selection set of generated documents- Schema extensions (
extend type,extend enum, ...) are merged schema_headersconfig option (with${ENV_VAR}expansion) and an introspection fallback (converted to SDL) for remote schemas- New error codes
BLD101(unknown fragment),BLD102(conflicting fragment),BLD103(unknown root field) - Test that compiles generated code (json_serializable +
dart analyze) and runs it against a fake link
Fixed #
- Subscriptions now compile: they return
Streams viaGraphQLClient.subscribe(previouslySubscriptionOptionswere passed toquery) - Generated code for schemas with unions no longer fails json_serializable
- Every operation in a document is generated, not only the first one
- Aliased root fields are read from the alias key
- Dart reserved words and clashing names are escaped (
class->class_, enum valuedefault->default_), keeping the GraphQL name on the wire - Typed
*Datamethods no longer swallow errors and returnnull;OperationException(withgraphqlErrors) is thrown instead @skip/@includeand other directives are kept when printing operations- Files whose path contains
schema.graphql(e.g.get_schema.graphql) are no longer skipped; schema files are detected by their content - The schema is loaded and
types.dartgenerated once per build instead of once per operation file; local schemas are read through the build system so editing them triggers a rebuild - Remote schema downloads have a 30s timeout; failures list every attempt
_dateTimeListToJsonnow serializes in UTC likeDateTimeConverter- Builder output uses
loginstead ofprint(which build_runner reported as warnings) - Generated files no longer produce analyzer warnings in user projects
dart analyze --fatal-infospasses again- Cross-platform path handling (Windows/macOS/Linux) in the builder and schema
downloader, including
file://URIs andindex.dartexports
Changed #
- Custom scalar wrapper classes keep the raw JSON value (
raw);valueis the string form (JSON-encoded for non-string values). The constructor accepts any JSON value DateTimeConverter.toJson/LocalDateConverter.toJsonreturndynamic- Unknown enum values throw
ArgumentErrorinstead ofException Decimal,Long,Byteand custom scalar classes implement==/hashCode- Mapped scalars (
scalar_mapping) no longer get a generated wrapper class
Removed #
- Placeholder
Awesomeclass
1.0.1 - 2025-08-31 #
Fixed #
- CI stability: make
dart analyze --fatal-infospass by removing an unused import in tests
1.0.0 - 2025-06-08 #
Added #
๐ฏ Core Features
- Type-safe code generation from GraphQL schemas and operations
- Automatic Dart class generation with JSON serialization support via
json_annotation - GraphQL client extensions for queries, mutations, and subscriptions
- Full null safety support with graceful error handling and safe defaults
๐ฆ Custom Scalar Support
DateTimewith automatic parsing andDateTimeConverterDecimalfor precise decimal numbers withDecimalConverterLongfor large integers withLongConverterBytefor byte values withByteConverterJSONfor arbitrary JSON data handling- Safe type converters for all basic types (
SafeBoolConverter,SafeIntConverter,SafeDoubleConverter,SafeStringConverter)
๐ Multiple Schema Sources
- Local file paths: relative and absolute paths
- HTTP/HTTPS URLs: direct GraphQL endpoint introspection
- File URLs:
file://protocol support - Alternative configuration: support for
schemaanddocumentsfield names
๐ง Advanced GraphQL Features
- Object types and interfaces with proper inheritance handling
- Union types with discriminated union support
- Enums with custom values and safe parsing
- Input types with validation and complex arguments
- Lists and nullable combinations (T, T?, [T], [T]?, [T?], [T?]?)
- Fragments and inline fragments for query optimization
- Custom directives handling
- Nested and recursive types with circular reference detection
๐ ๏ธ Development Tools
- Build system integration via
build_runnerandBuilderpattern - Flexible configuration through YAML files
- Glob pattern support for GraphQL document discovery
- Output directory customization
- Generated file management with proper imports and exports
๐งช Quality Assurance
- Comprehensive test coverage (85%+ coverage)
- Edge case handling for malformed GraphQL schemas
- Error recovery for invalid or missing data
- Type safety validation across all generated code
- Integration tests with real-world GraphQL schemas
Technical Implementation #
Code Generation Architecture
- Modular generator design with separate components:
TypeGenerator: Handles GraphQL type to Dart class conversionClientExtensionGenerator: Creates GraphQL client extension methodsSchemaAnalyzer: Parses and analyzes GraphQL schemasOperationAnalyzer: Processes GraphQL operations (queries, mutations, subscriptions)SchemaDownloader: Handles schema fetching from various sources
Generated Code Structure
// Example generated type
@JsonSerializable()
class User {
final String id;
final String? name;
@SafeBoolConverter()
final bool isActive;
@DateTimeConverter()
final DateTime? createdAt;
}
// Example generated extension
extension GraphQLClientExtensions on GraphQLClient {
Future<QueryResult<GetUser$Query>> getUserQuery({
required GetUserArguments variables,
}) async {
// Implementation with type safety
}
}
Error Handling Strategy
- Safe converters: All custom scalar converters include fallback values
- Null safety: Proper handling of nullable types with safe defaults
- Exception handling: Graceful degradation for schema parsing errors
- Validation: Input validation for all generated methods
Dependencies #
- Minimum Dart SDK: 3.0.0 (full null safety support)
- Flutter compatibility: 3.x and above
- Core dependencies:
json_annotation: ^4.8.1for serialization annotationsgraphql: ^5.1.3for GraphQL client operationsbuild: ^2.0.0for build system integrationyaml: ^3.1.0for configuration parsinggql: ^1.0.0for GraphQL AST parsing
Performance Optimizations #
- Lazy loading: Generated extensions are loaded only when needed
- Efficient parsing: Optimized GraphQL schema parsing with caching
- Minimal dependencies: Only essential packages included
- Tree-shaking friendly: Generated code supports dead code elimination
Documentation & Examples #
- Comprehensive README with detailed usage examples
- API documentation with inline code examples
- Configuration guide for all supported schema sources
- Testing examples and best practices
- Migration guide for future versions
Breaking Changes #
- This is the initial release, no breaking changes from previous versions
Migration Notes #
- For new projects: Follow the installation and configuration guide in README.md
- This package replaces manual GraphQL type definitions and client code
Known Limitations #
- Schema introspection may be slower for very large GraphQL schemas (>1000 types)
- Custom directives are parsed but not actively processed in type generation
- WebSocket subscriptions require additional client configuration
Note: This changelog follows Semantic Versioning and Keep a Changelog standards.