Data Types - Flutter SDK
On this page
The Flutter SDK supports Dart-language data types, a limited subset of BSON types, and UUID.
To learn how specific data types are mapped to BSON types in an App Services Schema, refer to Data Model Mapping in the Atlas App Services documentation.
Dart Types
Realm supports the following Dart types:
int
double
bool
String
DateTime
DateTime
When you use DateTime
with the Realm Flutter SDK, you can declare
it in the model as you would any other Dart type:
()class _Vehicle { () late ObjectId id; late String nickname; late DateTime dateLastServiced; }
However, it is important to note that Realm stores DateTime
in UTC.
When you use DateTime
, you must create it in UTC or convert it
with .toUtc()
before you store it. If your application requires it,
you can convert it back to local or the desired time zone when reading
from Realm.
// Create a Realm object with date in UTC, or convert with .toUtc() before storing final subaruOutback = realm.write<Vehicle>(() { return realm.add( Vehicle(ObjectId(), 'Subie', DateTime.utc(2022, 9, 18, 12, 30, 0))); }); final fordFusion = Vehicle(ObjectId(), 'Fuse', DateTime(2022, 9, 18, 8, 30, 0).toUtc()); realm.write(() { realm.add(fordFusion); }); // When you query the object, the `DateTime` returned is UTC final queriedSubaruOutback = realm.all<Vehicle>().query('nickname == "Subie"')[0]; // If your app needs it, convert it to Local() or the desired time zone final localizedSubieDateLastServiced = queriedSubaruOutback.dateLastServiced.toLocal();
Reference Realm Objects
You can also reference one or more Realm objects from another. Learn more in the relationship properties documentation.
Collections
A Realm collection contains zero or more instances of a Realm supported data type. In a Realm collection, all objects in a collection are of the same type.
You can filter and sort any collection using Realm's query language. Collections are live objects, so they always reflect the current state of the realm instance. The contents of a collection update when new elements are added to or deleted from the collection or from its Realm.
You can also listen for changes in the collection by subscribing to change notifications.
Realm has the following types of collections:
RealmList
Realm objects can contain lists of any supported data type. Realm uses the RealmList data type to store the data.
When you include RealmObjects
as the items in a RealmList
, it represents a to-many
relationship.
Deleting an object from the database will remove it from any RealmLists
where it existed. Therefore, a RealmList
of RealmObject
types will never contain null values.
Also, a RealmList
can contain multiple references to the same RealmObject
.
A RealmList
of primitive types can contain null values. If you do not
want to allow null values in a list, then either use non-nullable types in
the list declaration (for example, use List<int>
instead of
List<int?>
).
A RealmList
is mutable and you can add and remove elements on a RealmList
within a write transaction.
Add a RealmList to a Schema
You can add a RealmList
to your Realm Object schema by defining a property as type
List<T>
where T
can be any supported Realm data type
(except other collections), in your Realm Object model.
()class _Player { () late ObjectId id; late String username; // `inventory` property of type RealmList<Item> // where Items are other RealmObjects late List<_Item> inventory; // `traits` property of type RealmList<String> // where traits are Dart Strings. late List<String> traits; } ()class _Item { () late ObjectId id; late String name; late String description; }
Work with a RealmList
final artemis = realm.write(() => realm.add(Player(ObjectId(), 'Art3mis', inventory: [ Item(ObjectId(), 'elvish sword', 'sword forged by elves'), Item(ObjectId(), 'body armor', 'protects player from damage'), ], traits: [ 'brave', 'kind' ]))); // Use RealmList methods to filter results RealmList<String> traits = artemis.traits; final brave = traits.firstWhere((element) => element == 'brave'); final elvishSword = artemis.inventory.where((item) => item.name == 'elvish sword').first; // Query RealmList with Realm Query Language final playersWithBodyArmor = realm.query<Player>("inventory.name == \$0", ['body armor']); print("LEN " + playersWithBodyArmor.length.toString());
RealmSet
Realm objects can contain sets of any supported data type except another collection.
Realm uses the RealmSet data type
to store the data. In a RealmSet
collection, all values are unique.
RealmSet
extends the native Dart Set
data type with additional Realm-specific properties and methods.
When you include RealmObjects
as the items in a RealmSet
, it represents a to-many
relationship.
A RealmSet
is mutable and you can add and remove elements in a RealmSet
within a write transaction.
Add a RealmSet to a Schema
You can add a RealmSet
to your Realm Object schema by defining a property as type
Set<T>
where T
can be any supported Realm data type
except other collections, in your Realm Object model.
When defining a RealmSet in a schema:
A set of primitive types can be defined as either nullable or non-nullable. For example, both
Set<int>
andSet<int?>
are valid in a Realm schema.A set of
RealmObject
andRealmValue
types can only be non-nullable. For exampleSet<RealmValue>
is valid andSet<RealmValue?>
is not valid.You cannot define default values when defining a set in a schema. For example,
Set mySet = {0,1,2}
is not valid.
()class _RealmSetExample { late Set<String> primitiveSet; late Set<int?> nullablePrimitiveSet; late Set<_SomeRealmModel> realmObjectSet; } ()class _SomeRealmModel { late ObjectId id; }
Work with a RealmSet
The following example demonstrates some basic usage of RealmSet
.
For more information about all available methods, refer to the
RealmSet reference documentation on pub.dev.
final realm = Realm( Configuration.local([RealmSetExample.schema, SomeRealmModel.schema])); // Pass native Dart Sets to the object to create RealmSets final setExample = RealmSetExample( primitiveSet: {'apple', 'pear'}, nullablePrimitiveSet: {null, 2, 3}, realmObjectSet: {SomeRealmModel(ObjectId())}); // Add RealmObject to realm database realm.write(() => realm.add(setExample)); // Once you add Sets to the Realm, they are of type RealmSet RealmSet primitiveSet = setExample.primitiveSet; // Modify RealmSets of RealmObjects in write transactions realm.write(() { // Add element to a RealmSet with RealmSet.add() setExample.realmObjectSet.add(SomeRealmModel(ObjectId())); // Remove element from a RealmSet with RealmSet.remove() setExample.primitiveSet.remove('pear'); }); // Check if a RealmSet contains an element with RealmSet.contains() if (setExample.primitiveSet.contains('apple')) { print('Set contains an apple'); } // Query RealmSets using Realm Query Language final results = realm.query<RealmSetExample>('\$0 IN nullablePrimitiveSet', [null]); // Check number of elements in a RealmSet with RealmSet.length print(setExample.primitiveSet.length);
RealmResults
A RealmResults
collection represents the lazily-evaluated
results of a query operation. Unlike a RealmList
, results are immutable: you
cannot add or remove elements on the results collection.
This is because the contents of a results collection are determined by a
query against the database.
Realm.all() and Realm.query() return RealmResults
.
For more information on querying Realm, refer to Read Operations.
RealmResults<Player> players = realm.all<Player>(); RealmResults<Player> bravePlayers = realm.query<Player>('ANY traits == \$0', ['brave']);
Results are Lazily Evaluated
Realm only runs a query when you actually request the results of that query, e.g. by accessing elements of the results collection. This lazy evaluation enables you to write elegant, highly performant code for handling large data sets and complex queries.
Collections are Live
Like live objects, Realm collections are usually live:
Live results collections always reflect the current results of the associated query.
Live lists of
RealmObjects
always reflect the current state of the relationship on the realm instance.
There are two cases, however, when a collection is not live:
The collection is unmanaged: a
RealmList
property of a Realm object that has not been added to a realm yet or that has been copied from a realm.The collection is frozen.
Combined with listening for changes on a collection, live collections enable clean, reactive code. For example, suppose your view displays the results of a query. You can keep a reference to the results collection in your view class, then read the results collection as needed without having to refresh it or validate that it is up-to-date.
Important
Indexes may change
Since results update themselves automatically, do not store the positional index of an object in the collection or the count of objects in a collection. The stored index or count value could be outdated by the time you use it.
Additional Supported Data Types
ObjectId
ObjectId is a MongoDB-specific 12-byte unique value which you can use as an identifier for objects. ObjectId is indexable and can be used as a primary key.
To define a property as an ObjectId, set its type as ObjectId
in
your object model.
()class _ObjectIdPrimaryKey { () late ObjectId id; }
Call ObjectId()
to set any unique identifier properties of
your object. Alternatively, pass a string
to ObjectId()
to set the unique identifier property to a specific value.
final id = ObjectId(); final object = ObjectIdPrimaryKey(id);
UUID
UUID (Universal Unique Identifier) is a 16-byte unique value. You can use a UUID as an identifier for objects. UUIDs are indexable and you can use them as primary keys.
Note
Using UUID Instead of ObjectId
In general, you can use UUID
for any fields that function as a unique
identifier. Using UUID
might be particularly useful if you are migrating
data not stored in MongoDB since it is likely that your object's unique
identifiers are already of a UUID
type. Alternatively, using ObjectId
might be useful for a collection of data that already exists in MongoDB.
To define a property as a UUID, set its type as Uuid
in
your object model.
()class _UuidPrimaryKey { () late Uuid id; }
To set any unique identifier properties of
your object to a random value, call one of the Uuid
methods to create a UUID,
such as Uuid.v4()
.
final myId = Uuid.v4(); final object = UuidPrimaryKey(myId);
Decimal128
Dart doesn't have a native decimal type. You can use
Decimal128,
which is a 128-bit implementation of IEEE-754.
When defining a decimal type, use the Decimal128
BSON type.
When using Decimal128
, be aware that the Dart compareTo()
method
implements total ordering that mimics the Dart double
type. This means the
following things are true when using compareTo()
:
All
NaN
values are considered equal and greater than any numeric value.-Decimal128.zero
is less thanDecimal128.zero
(and the integer 0) but greater than any non-zero negative value.Negative infinity is less than all other values and positive infinity is greater than all non-NaN values.
All other values are compared using their numeric value.
RealmValue
The RealmValue
data type is a mixed data type that can represent any other valid Realm data type except a collection.
You can create collections of type RealmValue
, but a RealmValue
itself
cannot be a collection. RealmValue
is indexable, but cannot be a primary key.
Note
When defining your Realm object schema, you cannot create a nullable RealmValue
.
However, if you want a RealmValue
property to contain a null value,
you can use the special RealmValue.nullValue()
property.
To define a property as RealmValue
, set its type in your Realm object model.
()class _RealmValueExample { () late RealmValue singleAnyValue; late List<RealmValue> listOfMixedAnyValues; }
To add a RealmValue
to a Realm object, call RealmValue.from()
on the data.
final realm = Realm(Configuration.local([RealmValueExample.schema])); realm.write(() { realm.addAll([ RealmValueExample( singleAnyValue: RealmValue.from(1), listOfMixedAnyValues: [Uuid.v4(), "abc", 123].map(RealmValue.from)), RealmValueExample( singleAnyValue: RealmValue.nullValue(), listOfMixedAnyValues: ["abc", 123].map(RealmValue.from)) ]); });
Access the type of data with RealmValue.type
and the value with RealmValue.value
.
final data = realm.all<RealmValueExample>(); for (var obj in data) { if (obj.singleAnyValue.type == int) { print(obj.singleAnyValue.value.toString()); } }
Uint8List
Uint8List is a binary data type from dart:typed_data. You can use this binary data type in object models and property values.
To define a property as Uint8List
, you must first import dart:typed_data
.
Then, set the object's type as Uint8List
in your object model.
()class _BinaryExample { late String name; late Uint8List requiredBinaryProperty; var defaultValueBinaryProperty = Uint8List(8); late Uint8List? nullableBinaryProperty; }
To add Uint8List
to a Realm object, call Uint8List.fromList()
on the data.
final realm = Realm(Configuration.local([BinaryExample.schema])); realm.write(() { realm.addAll([ BinaryExample("Example binary object", Uint8List.fromList([1, 2])) ]); });
Embedded Objects
Realm treats each embedded object as nested data inside of a parent object. An embedded object inherits the lifecycle of its parent object. It cannot exist as an independent Realm object. Embedded objects have the following properties:
Embedded objects are deleted when their parent object is deleted or their parent no longer references them.
You cannot reassign an embedded object to a different parent object.
you cannot link to an embedded object from multiple parent objects.
You can only query an embedded object by accessing it through its parent object.
Declare an embedded objects by passing ObjectType.embeddedObject
to the @RealmModel()
annotation.
Embedded objects must be nullable when defining them in the parent object's
RealmModel
. You must also include the embedded object's schema in the realm's
Configuration.
The following example shows how to model an embedded object in a Realm schema.
The _Address
model is embedded within the _Person
model.
// The generated `Address` class is an embedded object. (ObjectType.embeddedObject)class _Address { late String street; late String city; late String state; late String country; } ()class _Person { () late ObjectId id; late String name; // Embedded object in parent object schema late _Address? address; // Must be nullable }
You can use the parent property to access the parent of the embedded object.
The following example shows the unique considerations when working with embedded objects.
The example uses the Address
embedded object generated from the _Address
RealmModel
in the above schema.
// Both parent and embedded objects in schema final realm = Realm(Configuration.local([Person.schema, Address.schema])); // Create an embedded object. final joesHome = Address("500 Dean Street", "Brooklyn", "NY", "USA"); final joePrimaryKey = ObjectId(); final joe = Person(joePrimaryKey, "Joe", address: joesHome); realm.write(() => realm.add(joe)); // Update an embedded object property. realm.write(() { joe.address?.street = "800 Park Place"; }); // Query a collection of embedded objects. // You must access the embedded object through the parent RealmObject type. final peopleWithNewYorkHomes = realm.query<Person>("address.state = 'NY'"); // Overwrite an embedded object. // Also deletes original embedded object from realm. final joesNewHome = Address("12 Maple Way", "Toronto", "ON", "Canada"); realm.write(() { joe.address = joesNewHome; }); // You can access the parent object from an embedded object. final thePersonObject = joesNewHome.parent; // Delete embedded object from parent object. realm.write(() => realm.delete(joe.address!)); // Add address back for the following example. final anotherNewHome = Address("202 Coconut Court", "Miami", "FL", "USA"); realm.write(() { joe.address = anotherNewHome; }); // Deleting the parent object also deletes the embedded object. realm.write(() => realm.delete(joe));
Example
The following model includes some supported data types.
part 'car.g.dart'; ()class _Car { () late ObjectId id; String? licensePlate; bool isElectric = false; double milesDriven = 0; late List<String> attributes; late _Person? owner; } // The generated `Address` class is an embedded object. (ObjectType.embeddedObject)class _Address { late String street; late String city; late String state; late String country; } ()class _Person { () late ObjectId id; late String name; // Embedded object in parent object schema late _Address? address; // Must be nullable }