mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-06 13:37:25 +00:00
Add [Dart] support (#4676)
* Add [Dart] support * fix enum vectors * Allow for opt out of string interning * fix comment style, make interning opt in * remove Offset<T>, prefer int * avoid creating unnecessary vtable objects * start work on tests - do not generate builder if struct has 0 fields - add int64 * support reading structs properly * correctly handle reading vectors of structs, dartfmt * support structs, fix unnecessary prepares * fix bool customizations * undo unintentional removal of file * docs updates, complete tutorial, bug fix for codegen * more documentation * Update docs, add to doxygen file * update package structure, add samples script/code * rearrange sample * Tests * Add readme for pub * cleanup package for pub * update docs for renamed file * remove custom matcher, use `closeTo` instead * remove unintentional file * remove unintended file checkin * use auto, move method, cleanup * refactor to ObjectBuilders, add Builders * Update tests, examples * Add files missing from previous commit * documentation and example updates * Update LICENSE, make dartanalyzer happy, fix minor bugs, get rid of duplicate files, publish script * fix sample for slightly different schema * Update pubspec.yaml
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
c43a0beff0
commit
88912640d0
@@ -30,6 +30,7 @@ Please select your desired language for our quest:
|
||||
<input type="radio" name="language" value="typescript">TypeScript</input>
|
||||
<input type="radio" name="language" value="php">PHP</input>
|
||||
<input type="radio" name="language" value="c">C</input>
|
||||
<input type="radio" name="language" value="dart">Dart</input>
|
||||
</form>
|
||||
\endhtmlonly
|
||||
|
||||
@@ -132,6 +133,9 @@ For your chosen language, please cross-reference with:
|
||||
<div class="language-c">
|
||||
[monster.c](https://github.com/dvidelabs/flatcc/blob/master/samples/monster/monster.c)
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
[example.dart](https://github.com/google/flatbuffers/blob/master/dart/example/example.dart)
|
||||
</div>
|
||||
|
||||
## Writing the Monsters' FlatBuffer Schema
|
||||
|
||||
@@ -312,6 +316,12 @@ Please be aware of the difference between `flatc` and `flatcc` tools.
|
||||
flatcc/samples/monster/build.sh
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.sh}
|
||||
cd flatbuffers/sample
|
||||
./../flatc --dart samples/monster.fbs
|
||||
~~~
|
||||
</div>
|
||||
|
||||
For a more complete guide to using the `flatc` compiler, please read the
|
||||
[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
|
||||
@@ -421,6 +431,14 @@ The first step is to import/include the library, generated files, etc.
|
||||
#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
import 'package:flat_buffers/flat_buffers.dart' as fb;
|
||||
|
||||
// Generated by `flatc`.
|
||||
import 'monster_my_game.sample_generated.dart' as myGame;
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Now we are ready to start building some buffers. In order to start, we need
|
||||
to create an instance of the `FlatBufferBuilder`, which will contain the buffer
|
||||
@@ -491,6 +509,15 @@ which will grow automatically if needed:
|
||||
flatcc_builder_init(B);
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// Create the fb.Builder object that will be used by our generated builders
|
||||
// Note that if you are only planning to immediately get the byte array this builder would create,
|
||||
// you can use the convenience method `toBytes()` on the generated builders.
|
||||
// For example, you could do something like `new myGame.MonsterBuilder(...).toBytes()`
|
||||
var builder = new fb.Builder(initialSize: 1024);
|
||||
~~~
|
||||
</div>
|
||||
|
||||
After creating the `builder`, we can start serializing our data. Before we make
|
||||
our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
|
||||
@@ -633,6 +660,51 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
|
||||
ns(Weapon_ref_t) axe = ns(Weapon_create(B, weapon_two_name, weapon_two_damage));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// The generated Builder classes work much like in other languages,
|
||||
final int weaponOneName = builder.writeString("Sword");
|
||||
final int weaponOneDamage = 3;
|
||||
|
||||
final int weaponTwoName = builder.writeString("Axe");
|
||||
final int weaponTwoDamage = 5;
|
||||
|
||||
final swordBuilder = new myGame.WeaponBuilder(builder)
|
||||
..begin()
|
||||
..addNameOffset(weaponOneName)
|
||||
..addDamage(weaponOneDamage);
|
||||
final int sword = swordBuilder.finish();
|
||||
|
||||
final axeBuilder = new myGame.WeaponBuilder(builder)
|
||||
..begin()
|
||||
..addNameOffset(weaponTwoName)
|
||||
..addDamage(weaponTwoDamage);
|
||||
final int axe = axeBuilder.finish();
|
||||
|
||||
|
||||
|
||||
// The genearted ObjectBuilder classes offer an easier to use alternative
|
||||
// at the cost of requiring some additional reference allocations. If memory
|
||||
// usage is critical, or if you'll be working with especially large messages
|
||||
// or tables, you should prefer using the generated Builder classes.
|
||||
// The following code would produce an identical buffer as above.
|
||||
final String weaponOneName = "Sword";
|
||||
final int weaponOneDamage = 3;
|
||||
|
||||
final String weaponTwoName = "Axe";
|
||||
final int weaponTwoDamage = 5;
|
||||
|
||||
final myGame.WeaponBuilder sword = new myGame.WeaponObjectBuilder(
|
||||
name: weaponOneName,
|
||||
damage: weaponOneDamage,
|
||||
);
|
||||
|
||||
final myGame.WeaponBuilder axe = new myGame.WeaponObjectBuilder(
|
||||
name: weaponTwoName,
|
||||
damage: weaponTwoDamage,
|
||||
);
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Now let's create our monster, the `orc`. For this `orc`, lets make him
|
||||
`red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him
|
||||
@@ -760,6 +832,26 @@ traversal. This is generally easy to do on any tree structures.
|
||||
inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// Serialize a name for our monster, called "Orc".
|
||||
final int name = builder.writeString('Orc');
|
||||
|
||||
// Create a list representing the inventory of the Orc. Each number
|
||||
// could correspond to an item that can be claimed after he is slain.
|
||||
final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
final inventory = builder.writeListUint8(treasure);
|
||||
|
||||
// The following code should be used instead if you intend to use the
|
||||
// ObjectBuilder classes:
|
||||
// Serialize a name for our monster, called "Orc".
|
||||
final String name = 'Orc';
|
||||
|
||||
// Create a list representing the inventory of the Orc. Each number
|
||||
// could correspond to an item that can be claimed after he is slain.
|
||||
final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
~~~
|
||||
</div>
|
||||
|
||||
We serialized two built-in data types (`string` and `vector`) and captured
|
||||
their return values. These values are offsets into the serialized data,
|
||||
@@ -863,6 +955,15 @@ offsets.
|
||||
ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// If using the Builder classes, serialize the `[sword,axe]`
|
||||
final weapons = builder.writeList([sword, axe]);
|
||||
|
||||
// If using the ObjectBuilders, just create an array from the two `Weapon`s
|
||||
final List<myGame.WeaponBuilder> weaps = [sword, axe];
|
||||
~~~
|
||||
</div>
|
||||
|
||||
<div class="language-cpp">
|
||||
<br>
|
||||
@@ -943,6 +1044,25 @@ for the `path` field above:
|
||||
// TBD
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// Using the Builder classes, you can write a list of structs like so:
|
||||
// Note that the intended order should be reversed if order is important.
|
||||
final vec3Builder = new myGame.Vec3Builder(builder);
|
||||
vec3Builder.finish(4.0, 5.0, 6.0);
|
||||
vec3Builder.finish(1.0, 2.0, 3.0);
|
||||
final int path = builder.endStructVector(2); // the lenght of the vector
|
||||
|
||||
// Otherwise, using the ObjectBuilder classes:
|
||||
// The dart implementation provides a simple interface for writing vectors
|
||||
// of structs, in `writeListOfStructs`. This method takes
|
||||
// `List<ObjectBuilder>` and is used by the generated builder classes.
|
||||
final List<myGame.Vec3ObjectBuilder> path = [
|
||||
new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0),
|
||||
new myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0)
|
||||
];
|
||||
~~~
|
||||
</div>
|
||||
|
||||
We have now serialized the non-scalar components of the orc, so we
|
||||
can serialize the monster itself:
|
||||
@@ -1095,6 +1215,58 @@ can serialize the monster itself:
|
||||
weapons, equipped, path));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// Using the Builder API:
|
||||
// Set his hit points to 300 and his mana to 150.
|
||||
final int hp = 300;
|
||||
final int mana = 150;
|
||||
|
||||
final monster = new myGame.MonsterBuilder(builder)
|
||||
..begin()
|
||||
..addNameOffset(name)
|
||||
..addInventoryOffset(inventory)
|
||||
..addWeaponsOffset(weapons)
|
||||
..addEquippedType(myGame.EquipmentTypeId.Weapon)
|
||||
..addEquippedOffset(axe)
|
||||
..addHp(hp)
|
||||
..addMana(mana)
|
||||
..addPos(vec3Builder.finish(1.0, 2.0, 3.0))
|
||||
..addPathOffset(path)
|
||||
..addColor(myGame.Color.Red);
|
||||
|
||||
final int orc = monster.finish();
|
||||
|
||||
// -Or- using the ObjectBuilder API:
|
||||
// Set his hit points to 300 and his mana to 150.
|
||||
final int hp = 300;
|
||||
final int mana = 150;
|
||||
|
||||
// Note that these parameters are optional - it is not necessary to set
|
||||
// all of them.
|
||||
// Also note that it is not necessary to `finish` the builder helpers above
|
||||
// - the generated code will automatically reuse offsets if the same object
|
||||
// is used in more than one place (e.g. the axe appearing in `weapons` and
|
||||
// `equipped`).
|
||||
final myGame.MonsterBuilder orcBuilder = new myGame.MonsterBuilder(
|
||||
name: name,
|
||||
inventory: treasure,
|
||||
weapons: weaps,
|
||||
equippedType: myGame.EquipmentTypeId.Weapon,
|
||||
equipped: axe,
|
||||
path: path,
|
||||
hp: hp,
|
||||
mana: mana,
|
||||
pos: new myGame.Vec3Builder(x: 1.0, y: 2.0, z: 3.0),
|
||||
color: myGame.Color.Red,
|
||||
path: [
|
||||
new myGame.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0),
|
||||
new myGame.Vec3ObjectBuilder(x: 4.0, y: 5.0, z: 6.0)
|
||||
]);
|
||||
|
||||
final int orc = orcBuilder.finish(builder);
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Note how we create `Vec3` struct in-line in the table. Unlike tables, structs
|
||||
are simple combinations of scalars that are always stored inline, just like
|
||||
@@ -1226,6 +1398,17 @@ Here is a repetition these lines, to help highlight them more clearly:
|
||||
ns(Monster_equipped_Weapon_add(B, axe));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// using the builder API:
|
||||
..addEquippedType(myGame.EquipmentTypeId.Weapon)
|
||||
..addEquippedOffset(axe)
|
||||
|
||||
// in the ObjectBuilder API:
|
||||
equippedTypeId: myGame.EquipmentTypeId.Weapon, // Union type
|
||||
equipped: axe, // Union data
|
||||
~~~
|
||||
</div>
|
||||
|
||||
After you have created your buffer, you will have the offset to the root of the
|
||||
data in the `orc` variable, so you can finish the buffer by calling the
|
||||
@@ -1291,6 +1474,12 @@ appropriate `finish` method.
|
||||
// Because we used `Monster_create_as_root`, we do not need a `finish` call in C`.
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// Call `finish()` to instruct the builder that this monster is complete.
|
||||
// See the next code section, as in Dart `finish` will also return the byte array.
|
||||
~~~
|
||||
</div>
|
||||
|
||||
The buffer is now ready to be stored somewhere, sent over the network, be
|
||||
compressed, or whatever you'd like to do with it. You can access the buffer
|
||||
@@ -1383,6 +1572,11 @@ like so:
|
||||
flatcc_builder_clear(B);
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
final Uint8List buf = builder.finish(orc);
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Now you can write the bytes to a file, send them over the network..
|
||||
**Make sure your file mode (or tranfer protocol) is set to BINARY, not text.**
|
||||
@@ -1490,6 +1684,12 @@ before:
|
||||
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
import 'package:flat_buffers/flat_buffers.dart' as fb;
|
||||
import './monster_my_game.sample_generated.dart' as myGame;
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Then, assuming you have a buffer of bytes received from disk,
|
||||
network, etc., you can create start accessing the buffer like so:
|
||||
@@ -1591,6 +1791,13 @@ won't work**
|
||||
// Note: root object pointers are NOT the same as the `buffer` pointer.
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
List<int> data = ... // the data, e.g. from file or network
|
||||
// A generated factory constructor that will read the data.
|
||||
myGame.Monster monster = new myGame.Monster(data);
|
||||
~~~
|
||||
</div>
|
||||
|
||||
If you look in the generated files from the schema compiler, you will see it generated
|
||||
accessors for all non-`deprecated` fields. For example:
|
||||
@@ -1611,7 +1818,7 @@ accessors for all non-`deprecated` fields. For example:
|
||||
</div>
|
||||
<div class="language-csharp">
|
||||
~~~{.cs}
|
||||
// For C#, unlike other languages support by FlatBuffers, most values (except for
|
||||
// For C#, unlike most other languages support by FlatBuffers, most values (except for
|
||||
// vectors and unions) are available as propreties instead of asccessor methods.
|
||||
var hp = monster.Hp
|
||||
var mana = monster.Mana
|
||||
@@ -1660,6 +1867,15 @@ accessors for all non-`deprecated` fields. For example:
|
||||
flatbuffers_string_t name = ns(Monster_name(monster));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
// For Dart, unlike other languages support by FlatBuffers, most values
|
||||
// are available as propreties instead of asccessor methods.
|
||||
var hp = monster.hp;
|
||||
var mana = monster.mana;
|
||||
var name = monster.name;
|
||||
~~~
|
||||
</div>
|
||||
|
||||
These should hold `300`, `150`, and `"Orc"` respectively.
|
||||
|
||||
@@ -1745,6 +1961,14 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`:
|
||||
float z = ns(Vec3_z(pos));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
myGame.Vec3 pos = monster.pos;
|
||||
double x = pos.x;
|
||||
double y = pos.y;
|
||||
double z = pos.z;
|
||||
~~~
|
||||
</div>
|
||||
|
||||
`x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively.
|
||||
|
||||
@@ -1811,6 +2035,12 @@ FlatBuffers `vector`.
|
||||
size_t inv_len = flatbuffers_uint8_vec_len(inv);
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
int invLength = monster.inventory.length;
|
||||
var thirdItem = monster.inventory[2];
|
||||
~~~
|
||||
</div>
|
||||
|
||||
For `vector`s of `table`s, you can access the elements like any other vector,
|
||||
except your need to handle the result as a FlatBuffer `table`:
|
||||
@@ -1885,6 +2115,13 @@ except your need to handle the result as a FlatBuffer `table`:
|
||||
uint16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1))));
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
int weaponsLength = monster.weapons.length;
|
||||
var secondWeaponName = monster.weapons[1].name;
|
||||
var secondWeaponDamage = monster.Weapons[1].damage;
|
||||
~~~
|
||||
</div>
|
||||
|
||||
Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created
|
||||
the `union`, we need to get both parts of the `union`: the type and the data.
|
||||
@@ -2008,6 +2245,18 @@ We can access the type to dynamically cast the data as needed (since the
|
||||
}
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
var unionType = monster.equippedType.value;
|
||||
|
||||
if (unionType == myGame.EquipmentTypeId.Weapon.value) {
|
||||
myGame.Weapon weapon = mon.equipped as myGame.Weapon;
|
||||
|
||||
var weaponName = weapon.name; // "Axe"
|
||||
var weaponDamage = weapon.damage; // 5
|
||||
}
|
||||
~~~
|
||||
</div>
|
||||
|
||||
## Mutating FlatBuffers
|
||||
|
||||
@@ -2083,6 +2332,11 @@ mutators like so:
|
||||
(except in-place vector sorting is possible).>
|
||||
~~~
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
~~~{.dart}
|
||||
<API for mutating FlatBuffers not yet available in Dart.>
|
||||
~~~
|
||||
</div>
|
||||
|
||||
We use the somewhat verbose term `mutate` instead of `set` to indicate that this
|
||||
is a special use case, not to be confused with the default way of constructing
|
||||
@@ -2192,5 +2446,8 @@ For your chosen language, see:
|
||||
<div class="language-c">
|
||||
[Use in C](@ref flatbuffers_guide_use_c)
|
||||
</div>
|
||||
<div class="language-dart">
|
||||
[Use in Dart](@ref flatbuffers_guide_use_dart)
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
Reference in New Issue
Block a user