florval 0.3.13
florval: ^0.3.13 copied to clipboard
Generate type-safe Flutter/Dart API clients from OpenAPI specs. Freezed models, status-code Union types, and Riverpod 3.x integration.
0.3.13 #
Documentation #
- Rewrite the README to lead with the value proposition: Restructured the README so it opens with what florval does and why it matters (OpenAPI → dio + freezed + Riverpod 3.x with status-code Union types) instead of burying the pitch below setup details. Verified and tightened the competitor comparison table against the current state of swagger_parser and other generators, fixed non-compiling Dart 3
switchsamples, corrected thePaginatedDatafile path, and documented the Dio wiring seam and real-world setup. No generator behavior changes.
0.3.12 #
Bug Fixes #
- Fix invalid Dart from paginated
fetchMorehelper with no extra params: The generatedfetchMore...pagination helper always emitted a named-argument block (MutationTarget ref, { ... })), even for endpoints with no path/query parameters besides the excluded cursor. This produced an empty{ }block (MutationTarget ref, {\n}) {), which is a Dart syntax error (Expected an identifier.), sodart formatandbuild_runnerfailed in consumer projects. The helper now branches on whether any additional params exist: with params it emits the named block as before; with none it emits only the positionalMutationTarget refargument.
0.3.11 #
Bug Fixes #
- Serialize
format: date-timefields as UTC: DateTime fields with OpenAPIformat: date-timewere serialized with a baretoIso8601String()on the localDateTime, producing a timezone-less string (e.g.2026-05-30T18:00:00.000) with noZ/offset. A server running in UTC then interprets that local wall-clock time as UTC, so every value is off by the device's timezone offset. The generator now emits aDateTimeUtcConverter(JsonConverter<DateTime, String>) incore/date_serializer.dartthat serializes viatoUtc().toIso8601String()(with aZsuffix), annotates non-absentableformat: date-timefields with@DateTimeUtcConverter(), and inlinestoUtc().toIso8601String()in the customtoJsonof absentable (PATCH/PUT) models. Deserialization is unchanged (DateTime.parse).format: datefields are unaffected.
0.3.10 #
Features #
- Serialize
format: datefields asyyyy-MM-dd: DateTime fields with OpenAPIformat: datewere serialized as full ISO 8601 strings (e.g.2026-05-26T00:00:00.000) instead of date-only strings (2026-05-26). The generator now preserves the OpenAPIformatonFlorvalType, emits aDateOnlyConverter(JsonConverter<DateTime, String>) incore/date_serializer.dart, and annotatesformat: datefields with@DateOnlyConverter()so json_serializable producesyyyy-MM-ddoutput. Absentable (PATCH/PUT) models withformat: datefields also serialize correctly via inline date formatting in the customtoJson
0.3.9 #
Bug Fixes #
- Wrap
List<MultipartFile>in backticks in_isMultipartFileListTypedoc comment: The doc comment onClientGenerator._isMultipartFileListTypereferencedList<MultipartFile>andMultipartFilewithout backticks, so dartdoc interpreted the angle brackets as HTML tags. pana reportedINFO: Angle brackets will be interpreted as HTML.and the pub.dev score lost 10 points on "code has no errors, warnings, lints, or formatting issues". Both type references are now wrapped in backticks so they render as inline code - Wrap single-line
ifreturns inflorval_runner_absentable_test.dartin braces: Severalif (...) return false;statements in theisComplexObjectFieldtest helper triggered thecurly_braces_in_flow_control_structureslint. All branches now use explicit{ ... }bodies sodart analyzereports 0 issues
0.3.8 #
Bug Fixes #
- Fix
Unexpected fielderror onList<MultipartFile>multipart uploads: When a multipart endpoint had aList<MultipartFile>form field, the generated client passed it throughFormData.fromMap({...}), which uses Dio's defaultListFormat.multiCompatibleand appends[]to the field name (e.g.images[]instead ofimages). Servers that require strict field-name matching (NestJS multerFilesInterceptor, Express/Fastify equivalents, etc.) rejected such requests with HTTP 400 "Unexpected field". The generator now emits a_formDatalocal that callsformData.files.add(MapEntry(name, file))for each file, preserving the exact form field name. Single-MultipartFilefields and other multipart shapes are unchanged.
0.3.7 #
Bug Fixes #
- Drop unnecessary
asyncon paginatedfetchMoremutation closure: The closure passed toMutation.runfor paginatedfetchMore...helpers was generated withasynceven though it only returnednotifier.loadNextPage()directly without usingawait, triggering theunnecessary_asynclint in consumer projects. The closure is now generated without theasyncmodifier
0.3.6 #
Bug Fixes #
- Wrap paginated
loadNextPage()early return in braces: Generated paginated providers emittedif (!_hasMore || _nextCursor == null) return state.requireValue;on a single line, triggering thealways_put_control_body_on_new_linelint in consumer projects. The body is now placed on a new line inside{ ... }
0.3.5 #
Bug Fixes #
- Wrap single-statement
ifblocks in braces: The_isComplexObjectFieldmethod hadifstatements with single-line return statements, triggering thecurly_braces_in_flow_control_structuresDart lint. Allifstatements now properly wrap their bodies in braces for full lint compliance
0.3.4 #
Bug Fixes #
- Apply
JsonOptional<T>to DTO schemas referenced frommultipart/form-dataPATCH/PUT endpoints: When a PATCH/PUT endpoint usedmultipart/form-datawith a complex object form field (e.g.updateUserDto: {$ref: '#/components/schemas/UpdateUserDto'}alongside a binaryiconFile), the referenced DTO's optional fields were not wrapped inJsonOptional<T>because_markAbsentableFields()unconditionally excluded all multipart request bodies. Now iterates multipart form fields and marks any complex object DTO (named$ref, inline object, orallOf-wrapped schema) as absentable, generating proper@Freezed(fromJson: false, toJson: false)withcontainsKey-based 3-state fromJson/toJson
0.3.3 #
Bug Fixes #
- Fix nullable lost on
allOf/$refschemas withouttypefield: When an OpenAPI 3.0 property usednullable: truewithallOf(e.g.nullable: true, allOf: [{$ref: '#/components/schemas/Foo'}]), the v3.0→v3.1 normalizer silently discarded the nullable information because it only convertednullablewhen an explicittypefield was present. Now properly convertsnullable: truetotype: ['null']for all schemas, ensuring nullable$refproperties generateT?instead ofrequired T
0.3.2 #
Bug Fixes #
- Serialize complex object fields in
multipart/form-dataas JSON: Non-binary complex types ($refor object schemas) in multipart form fields were passed directly toFormData.fromMap(), causing dio to call.toString()instead of properly serializing the data. Now generatesMultipartFile.fromString(jsonEncode(dto.toJson()), contentType: MediaType('application', 'json'))for such fields. Also handles enum fields with.jsonValueand list-of-enum fields with.map((e) => e.jsonValue).toList(). Conditionally addsdart:convertandhttp_parserimports only when complex multipart fields are present
0.3.1 #
Features #
exclude_auto_invalidateoption: Skip auto-invalidation for specific mutations by operationId. Useful for optimistic updates or mutations where cache invalidation should be handled manually. Configure viariverpod.exclude_auto_invalidatelist inflorval.yaml
0.3.0 #
Breaking Changes #
- Centralize API Dio provider: Generate a single
apiDioProviderinstead of per-tag client providers that throwUnimplementedError. Each client provider now usesref.watch(apiDioProvider)to obtain its Dio instance. Users only need one override inProviderScopeinstead of N per-tag overrides. Migration: replace individual client provider overrides withapiDioProvider.overrideWithValue(yourDio)
0.2.13 #
Bug Fixes #
- Add missing imports for multipart form field
$reftypes: When amultipart/form-datarequest body contained fields referencing$reftypes (e.g.UpdateUserDto), the generated provider and client files did not include import statements for those model types, causingUndefined classerrors. Both_collectModelImports()methods now iterateformFieldsto collect imports for multipart requests
0.2.12 #
Bug Fixes #
- Fix multipart form
$reffields resolving to wrong type name: When amultipart/form-datarequest body contained a field with$ref(e.g.updateUserDto: $ref: '#/components/schemas/UpdateUserDto'), the generated code produced an incorrect contextName-based type likeUserControllerUpdateV1UpdateUserDtoinstead of the referencedUpdateUserDto. The cause wasresolveSchema()being called too early, stripping the$refbeforeschemaToTypecould detect it
0.2.11 #
Bug Fixes #
- Promote inline enums everywhere and serialize params via
jsonValue: Inlineenumschemas (e.g.type: string, enum: [...]written directly in a parameter, request body, multipart form field, array element,additionalProperties, or pagination wrapper field) were silently generated asString/intbecauseSchemaAnalyzer.schemaToTypeneeds acontextNameto name the anonymous enum and several call sites did not pass one.contextNameis now required, with a documented naming convention (${OperationId}${ParamName}for params,${ParentContext}Item/${ParentContext}Valuefor array/map children, etc.), so every call site articulates a name and inline enums are always promoted to first-class Dartenumtypes - Serialize enum query/path parameters via
.jsonValueinstead of.name: The generated client used.name(the Dart identifier) for enum query params and relied ontoString()for enum path params. For enum values whose OpenAPI string differs from the Dart identifier (e.g."in-progress"→inProgress) this produced the wrong value on the wire. Switch to.jsonValue, which returns the original OpenAPI string, and interpolate enum path params as${x.jsonValue}
0.2.10 #
Bug Fixes #
- Always generate mutation helper functions regardless of
autoInvalidate: Whenauto_invalidatewasfalse, mutation helper functions were not generated at all. Now helpers are always generated —autoInvalidateonly controls whetherref.container.invalidate()calls are included
0.2.9 #
Bug Fixes #
- Add braces to retry function
if-statement: Single-lineif (...) return null;triggers thestatement_on_same_lineDart lint. Wrap in braces for compliance
0.2.8 #
Bug Fixes #
- Remove unnecessary
asyncfrom GET providerbuild()methods: Thebuild()methods in GET providers just returnclient.methodName()withoutawait, so theasyncmodifier was unnecessary and triggered theunnecessary_asyncDart lint warning
0.2.7 #
Bug Fixes #
- Remove unnecessary
asyncfrom mutation helper functions: The outer helper functions just returnmutation.run()withoutawait, so theasyncmodifier was unnecessary and triggered theunnecessary_asyncDart lint warning
0.2.6 #
Bug Fixes #
- Fix dio method calls to use explicit type arguments: Add explicit type parameters (e.g.,
<Map<String, dynamic>>,<dynamic>) to all generateddio.get(),dio.post(),dio.put(),dio.patch(), anddio.delete()calls for improved type safety. Calls without a response body now use<dynamic>instead of<void>to match dio's expected return type
0.2.5 #
Features #
- Paginated
fetchMoreas Mutation pattern: Convert paginated provider'sfetchMore()to Riverpod 3.x Mutation pattern. The Notifier now exposesloadNextPage()as an internal state-management method, while an externalMutation<PaginatedData<T, P>>()constant andfetchMoreXxx(MutationTarget ref)helper function are generated alongside it. This enablesMutationState-based duplicate call prevention and loading state tracking from the UI - Dot-notated
next_cursor_fieldin pagination config: Support nested field paths likepagination.nextCursorinflorval.yamlpagination config. The analyzer resolves dot-separated segments through nested schema properties (includingallOf-wrapped$ref), and the generator emits chained property access (e.g.,data.pagination.nextCursor)
Bug Fixes #
- Fix
PaginatedData<dynamic, ...>type inference inloadNextPage(): Add explicit type parameters toPaginatedData<T, P>(...)constructor calls in generated paginated providers. Without them, Dart's type inference falls back todynamicwhen thePaginatedDataresult is assigned to a local variable before being passed tostate = AsyncData(result) - Fix Family provider
.notifieraccess in paginated Mutation helpers: Pass build parameters (path/query params) through to the Mutation helper function so it can construct the correct Family provider instance (e.g.,xxxProvider(eventId: eventId).notifier) instead of calling.notifierdirectly on the Family type - Fix
allOf-wrapped$refresolution in pagination field validation: When validatingnext_cursor_fieldexistence, resolveallOfcompositions by merging properties from all entries, not just following top-level$ref. This fixes pagination detection for OpenAPI specs where nested objects use the common NestJSallOf: [$ref: ...]pattern - Fix
readOnly/writeOnlytest assertions in absentable schemas: Test was checking the entire generated code for absence ofjson['field'], but the field correctly appeared in the counterpart method (fromJsonfor readOnly,toJsonfor writeOnly). Narrow assertions to check only the relevant method body
Refactoring #
- Move
_dotFieldAccessfrom top-level private function toProviderGeneratorstatic method - Fix E2E test assertion for Mutation constant that was broken by
dart formatline wrapping
0.2.4 #
Features #
- Auto-apply
dart fixanddart formaton generated code: Rundart fix --applyfollowed bydart formatas a post-processing step after code generation. This automatically resolves lint violations (directives_ordering,prefer_const_constructors, etc.) without modifying individual generators
Improvements #
- Reformat generated API client methods: Improve readability and consistency of generated API client code by aligning parameters and return statements
0.2.3 #
Bug Fixes #
- Fix ambiguous exports for union type subclass names: Resolve
dart analyzeerrors caused by union variant subclass names (e.g.,RequestDataRoomInvitation) being exported from both the union type file and a standalone model file. Implemented 3-layer defense:- Clean output directory before generation to remove stale files from previous runs
- Pre-filter
variantSchemaNames()to exclude discriminator union variants from standalone model generation - Barrel-level deduplication via
unionSubclassNames()as a safety net to remove colliding names from exports
- Fix variant schema filtering for inline unions: Only use component schemas (not inline union schemas from response bodies) for variant filtering, preventing independent component schemas like
EventNotFoundErrorDtofrom being incorrectly excluded
0.2.2 #
Improvements #
- Update package description to be more concise and highlight core features (Freezed models, status-code Union types, Riverpod 3.x)
- Add encer.co.jp vision statement to README
0.2.1 #
Features #
- Inline enum generation: Generate Dart enums from inline OpenAPI
enumproperties, instead of requiring top-level schema definitions readOnly/writeOnlysupport: Read OpenAPIreadOnlyandwriteOnlyflags on schema fields and propagate them to the intermediate representation- Doc comments and example output: Generate
///doc comments from OpenAPIdescriptionfields and/// Example: ...fromexamplevalues on models, fields, enums, and endpoints defaultvalue support: Read OpenAPIdefaultvalues and generate@Default(...)annotations in freezed models. Supports string, integer, number, boolean, empty array, and enum types. DateTime and non-empty array defaults emit a warning and are skipped. Fields with bothrequiredanddefaultuse@Default(...)(freezed does not allow both)deprecatedflag support: Read OpenAPIdeprecatedflags at schema, property, operation, and parameter levels and generate@Deprecated('')annotations. Schema-level deprecated applies to class/enum definitions; property-level deprecated applies to individual fields; operation/parameter-level deprecated applies to client methods and their parameters- Schema title fallback for doc comments: Use schema
titleas doc comment whendescriptionis not available
Bug Fixes #
- Preserve properties in
_applyAbsentable: Fixexample,readOnly, andwriteOnlyfields being lost when_applyAbsentabletransforms optional fields for PATCH/PUT request bodies - Operation/Parameter deprecated field name: Fix incorrect property name used when reading the
deprecatedflag from Operation and Parameter objects - Null safety in doc comment generation: Fix null safety operator for
descriptionandexamplein doc comment generation
0.2.0 #
Breaking Changes #
- Rename response Union types from
{Op}Responseto{Op}ApiResponseto structurally resolve name collisions with model classes - Switch response Union types from freezed to plain Dart sealed classes (copyWith/equality/fromJson not needed, eliminates build_runner dependency for response types)
Features #
- JsonOptional<T> for PATCH/PUT partial updates: Generate
JsonOptional<T>sentinel type that distinguishes "key absent" (unchanged) from "key is null" (clear value) at the type level. Optional fields in PATCH/PUT request bodies are wrapped inJsonOptional<T>with@Default(JsonOptional<T>.absent()). CustomfromJsonusesjson.containsKey()for 3-state restoration; customtoJsonexcludes absent keys.@Freezed(fromJson: false, toJson: false)bypasses json_serializable entirely for absentable schemas. POST request bodies and response models are unaffected. - Inline anyOf/oneOf union type support: Detect anyOf/oneOf declared inline within schema properties and automatically generate Union types for them
- Restore freezed for discriminator union types: Generate
@Freezed(unionKey: '...')+@FreezedUnionValue('...')with variant fields inlined into factory constructors - Non-discriminator union fromJson/toJson: For oneOf/anyOf without a discriminator, generate fromJson that tries each variant sequentially and toJson that branches on runtimeType
- Logger warnings: Emit warnings when an array type is missing
itemsor when an unsupported content-type is encountered
Bug Fixes #
- anyOf/oneOf nullable
$refpattern: FixanyOf: [{$ref: '#/.../Foo'}, {type: 'null'}]incorrectly resolving toMap<String, dynamic>instead of a nullable type - additionalProperties typed Map: Generate correct
Map<String, T>type whenadditionalPropertiesspecifies a schema - ResponseAnalyzer nullable anyOf: Fix false detection of nullable anyOf patterns during response analysis
_extractTypedynamic fallback: Fix dynamic fallback handling when type extraction fails- Inline object fallback: Fix inline object schemas incorrectly falling back to
Map<String, dynamic>instead of generating proper types - Inline oneOf/anyOf responses: Fix inline oneOf/anyOf in endpoint response definitions falling back to
Map<String, dynamic> - allOf + nullable
$ref: Fix resolution of nullable$refentries within allOf compositions List<dynamic>type error: Fix type inference error inflorval_runnerschemas variable- Name collision structural fix: Resolve response Union type vs. model class name collisions using import prefix splitting and barrel file separation
json_serializablesealed class error: Fixjson_serializableerrors on sealed class fields with discriminator.g.dartpart directive exclusion: Skip unnecessary.g.dartpart directive generation for sealed classes with discriminator- Library import prefix lint: Remove leading underscores from generated import prefixes to fix
no_leading_underscores_for_library_prefixeslint violation includeIfNull: falserevert: Revert@JsonSerializable(includeIfNull: false)addition due to incompatibility with demo-api default values; fix applied on API side instead
Refactoring #
- Analyzers as pure functions: Convert
SchemaAnalyzer,EndpointAnalyzer, andResponseAnalyzerto stateless pure functions for improved testability (+477/-305 lines) - FlorvalRunner phase splitting: Clearly separate Parse → Resolve → Analyze → Generate → Write phases with an
AnalysisResultintermediate representation - Generator layer deduplication: Extract shared status code conversion and import collection logic from
client_generator/response_generatorintoutils/status_code.dartandutils/import_collector.dart(+72/-115 lines) - Import deduplication: Reduce unnecessary import generation in provider generator
- Test readability improvements: Unify formatting and structure in
model_generator_test, remove unnecessary forced unwraps inresponse_analyzer_test - Deduplicate toJson serialization logic: Unify
_writeToJsonFieldand_toJsonValueExpressionin ModelGenerator —_writeToJsonFieldnow delegates to_toJsonValueExpression, eliminating duplicated type-dispatch logic (DateTime, enum, List, reference types) - Barrel file separation: Exclude
api_responses.dartfromapi.dartre-exports, keeping only models/clients/providers
Demo / Example #
- Add demo-api server: Hono + @hono/zod-openapi based mock API server with full endpoint coverage (auth, CRUD, file upload, notifications)
- Add Flutter Web project: Flutter Web example app connecting to demo-api at localhost:3000
- Add CLI verification script:
example/lib/verify.dartfor integration testing against demo-api - Update example generated code: Regenerate all code based on demo-api OpenAPI spec (6 tags, 15 response types, 35 models)
- Remove unused files: Delete petstore-derived API client, model, provider, and response files
0.1.1 #
Bug Fixes #
- Fix mutation helpers missing query parameters
- Fix List request body calling
toJson()which doesn't exist onList - Fix empty-property schema causing
build_runnererror - Fix double
??in generated nullable optional parameters - Fix null-aware operator on enum query params inside null-check guard
- Fix Schema enum property name:
$enum→enumValues
Improvements #
- Expand Riverpod reserved name handling with generated super params
- Rename Riverpod reserved param names in generated providers
- Handle non-ASCII characters in field names and enum values
- Generate mutation helper functions even when no GET endpoints exist in tag
- Remove unnecessary
dart:asyncimport from generated providers - Skip unused request body imports in mutation-only providers
- Always generate
partdirective andriverpod_annotationimport in providers
Other #
- Remove deprecated pagination examples and related schemas from openapi.yaml
0.1.0 #
- Initial release
- OpenAPI 3.0 / 3.1 spec parsing with automatic v3.0 → v3.1 normalization
- Swagger 2.0 partial support (auto-normalized to 3.1)
$refresolution with circular reference detection- freezed 3.x data model generation (abstract class)
- Status-code Union type generation (freezed sealed class)
- Dart 3 switch expression support (no when/map)
- dio API client generation (no Retrofit dependency)
- DioException handling with status-code routing
- Riverpod 3.x provider generation (optional)
- GET →
@riverpodNotifier with build() parameters - POST/PUT/DELETE/PATCH →
Mutation<T>()constants - Auto-invalidation of GET providers after mutations
@Riverpod(retry:)support from config
- GET →
- Cursor-based pagination with
fetchMore()andPaginatedData<T> - multipart/form-data support with
MultipartFile - oneOf / anyOf / allOf / discriminator support
- Watch mode for auto-regeneration on spec changes
florval initcommand for config template generationflorval.yamlconfiguration with validation- Custom template headers and imports
- Tag-based endpoint grouping
- Barrel file generation