title: Dart sidebar_position: 10 id: dart license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Dart output is two files per schema: a main .dart file with annotated types and the IDL module owner, and a .fory.dart part file with generated serializers and metadata.
<dart_out>/package/package.dart<dart_out>/package/package.fory.dartMessages generate @ForyStruct annotated final class declarations with @ForyField on each field:
@ForyStruct() final class Person { Person(); @ForyField(id: 1) String name = ''; @ForyField(id: 2, type: Int32Type()) int id = 0; @ForyField(id: 7) List<Person_PhoneNumber> phones = <Person_PhoneNumber>[]; @ForyField(id: 8) Animal pet = Animal._empty(); }
Enums generate Dart enum declarations with a rawValue getter and fromRawValue factory:
enum Person_PhoneType { mobile, home, work; int get rawValue => switch (this) { Person_PhoneType.mobile => 0, Person_PhoneType.home => 1, Person_PhoneType.work => 2, }; static Person_PhoneType fromRawValue(int value) => switch (value) { 0 => Person_PhoneType.mobile, 1 => Person_PhoneType.home, 2 => Person_PhoneType.work, _ => throw StateError('Unknown Person_PhoneType raw value $value.'), }; }
Unions generate @ForyUnion annotated classes with factory constructors, a case enum, and a custom serializer:
enum AnimalCase { dog, cat; int get id => switch (this) { AnimalCase.dog => 1, AnimalCase.cat => 2, }; } @ForyUnion() final class Animal { final AnimalCase _case; final Object? _value; const Animal._(this._case, this._value); factory Animal.dog(Dog value) => Animal._(AnimalCase.dog, value); factory Animal.cat(Cat value) => Animal._(AnimalCase.cat, value); bool get isDog => _case == AnimalCase.dog; Dog get dogValue => _value as Dog; // ... }
Nested types use flat underscore naming (e.g., Person_PhoneNumber, Person_PhoneType).
list<T> fields generate ordered collection carriers and use the Fory list protocol. array<T> fields generate dense one-dimensional bool or numeric carriers and use the specialized dense-array protocol. Generated code must not choose array<T> only because a language has an optimized list-like carrier; the schema kind comes from the IDL.
| IDL schema | Dart generated carrier | Notes |
|---|---|---|
list<int32> | List<int> | List protocol, varint element encoding |
list<fixed int32> | List<int> | List protocol, fixed-width element segment |
array<bool> | BoolList | One byte per bool |
array<int8> | Int8List | Dense signed bytes |
array<int16> | Int16List | Dense little-endian int16 |
array<int32> | Int32List | Dense little-endian int32 |
array<int64> | Int64List | Dense little-endian int64 |
array<uint8> | Uint8List | Dense unsigned bytes |
array<uint16> | Uint16List | Dense little-endian uint16 |
array<uint32> | Uint32List | Dense little-endian uint32 |
array<uint64> | Uint64List | Dense little-endian uint64 |
array<float16> | Float16List | Dense binary16 storage |
array<bfloat16> | Bfloat16List | Dense bfloat16 storage |
array<float32> | Float32List | Dense little-endian float32 |
array<float64> | Float64List | Dense little-endian float64 |
Generated Dart fields that use ArrayType(element: BoolType()) must use BoolList; plain List<bool> remains the generated and handwritten carrier for list<bool>.
Reference tracking on list elements or map values uses the container sugar annotations:
@ListField(element: DeclaredType(ref: true)) @ForyField(id: 3) List<Node> children = <Node>[]; @MapField(value: DeclaredType(ref: true)) @ForyField(id: 2) Map<String, Node> byName = <String, Node>{};
Each generated Dart IDL library includes a module owner named after the input file, such as AddressbookForyModule for addressbook.dart. The module installs imported modules first and then registers every local schema type with its default IDL identity:
abstract final class AddressbookForyModule { static void install(Fory fory) { complex_pb.ComplexPbForyModule.install(fory); _registerType(fory, Person); _registerType(fory, Dog); } static Fory getFory() { ... } static void _registerType(Fory fory, Type type) { if (type == Person) { registerGeneratedStruct(fory, _personForySchema, id: 100, namespace: null, typeName: null); return; } // ... other types } }
import 'package:fory/fory.dart'; import 'generated/addressbook/addressbook.dart'; void main() { final fory = Fory(); AddressbookForyModule.install(fory); final person = Person() ..name = 'Alice' ..id = 1; final bytes = fory.serialize(person); final roundTrip = fory.deserialize<Person>(bytes); }
With --grpc, Dart emits one <stem>_grpc.dart per schema containing <Service>Client and <Service>ServiceBase. The service base registers its own method descriptors, so no separate registrar is generated. See Dart gRPC for generated module naming, Fory installation, and usage.