Flamingo
[Flutter Library] Flamingo is a firebase firestore model framework library. ๐ค
Install / Use
/learn @hukusuke1007/FlamingoREADME
Flamingo
Flamingo is a firebase firestore model framework library.
https://pub.dev/packages/flamingo
Example code
See the example directory for a complete sample app using flamingo.
Installation
Add this to your package's pubspec.yaml file:
dependencies:
flamingo:
flamingo_annotation:
dev_dependencies:
build_runner:
flamingo_generator:
Setup
Please check Setup of cloud_firestore.<br> https://pub.dev/packages/cloud_firestore
Usage
Adding a initializeApp code to main.dart.
Initialize
import 'package:flamingo/flamingo.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Flamingo.initializeApp();
...
}
Create Model
Create class that inherited Document. And add json mapping code into override functions.
Can be used flamingo_generator. Automatically create code for mapping JSON.
import 'package:flamingo/flamingo.dart';
import 'package:flamingo_annotation/flamingo_annotation.dart';
part 'user.flamingo.dart';
class User extends Document<User> {
User({
String? id,
DocumentSnapshot<Map<String, dynamic>>? snapshot,
Map<String, dynamic>? values,
}) : super(id: id, snapshot: snapshot, values: values);
@Field()
String? name;
@override
Map<String, dynamic> toData() => _$toData(this);
@override
void fromData(Map<String, dynamic> data) => _$fromData(this, data);
}
Annotation list.
- @Field()
- @StorageField()
- @ModelField()
- @SubCollection()
Execute build runner to generate data mapping JSON.
flutter pub run build_runner build
It will be generated the following code.
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user.dart';
// **************************************************************************
// FieldValueGenerator
// **************************************************************************
/// Field value key
enum UserKey {
name,
}
extension UserKeyExtension on UserKey {
String get value {
switch (this) {
case UserKey.name:
return 'name';
default:
throw Exception('Invalid data key.');
}
}
}
/// For save data
Map<String, dynamic> _$toData(User doc) {
final data = <String, dynamic>{};
Helper.writeNotNull(data, 'name', doc.name);
return data;
}
/// For load data
void _$fromData(User doc, Map<String, dynamic> data) {
doc.name = Helper.valueFromKey<String>(data, 'name');
}
[Option] build.yaml
If you set build.yaml in the root of the project, the automatic generation will be faster.
https://github.com/hukusuke1007/flamingo/blob/master/flamingo/example/build.yaml
targets:
$default:
builders:
flamingo_generator|field_value_generator:
generate_for:
include:
- lib/model/*.dart
- lib/model/**/*.dart
CRUD
Create instance the following code.
final user = User();
print(user.id); // id: Automatically create document id;
final user = User(id: 'userId');
print(user.id); // id: 'userId'
Using DocumentAccessor or Batch or Transaction in order to CRUD.
final user = User()
..name = 'hoge';
final documentAccessor = DocumentAccessor();
// save
await documentAccessor.save(user);
// update
await documentAccessor.update(user);
// delete
await documentAccessor.delete(user);
// Batch
final batch = Batch()
..save(user)
..update(user);
..delete(user);
await batch.commit();
If save a document, please check firestore console.
<a href="https://imgur.com/tlmwnrr"><img src="https://i.imgur.com/tlmwnrr.png" width="90%" /></a>
And can be used field value key and save data by specific key.
final documentAccessor = DocumentAccessor();
await documentAccessor.saveRaw(
<String, dynamic>{ UserKey.name.value: 'hogehoge' },
user.reference,
);
Get a document.
final user = await documentAccessor.load<User>(User(id: 'userId'));
Get a document from cache.
final user = await documentAccessor.loadCache<User>(User(id: 'userId'));
Get a document from cache and server.
String name = 'Anonymous';
final user = await documentAccessor.load<User>(
User(id: 'userId'),
fromCache: (cache) {
setState(() {
// 1. update state from cache
if (cache != null) {
name = cache.name;
}
});
},
);
setState(() {
// 2. update state from serverAndCache
if (user != null) {
name = user.name;
}
});
Get Collection Documents
CollectionPaging
Can be used get and paging features of documents by CollectionPaging.
Query of Collection.
final collectionPaging = CollectionPaging<User>(
query: User().collectionRef.orderBy('createdAt', descending: true),
limit: 20,
decode: (snap) => User(snapshot: snap),
);
// Load
List<User> items = await collectionPaging.load();
// LoadMore
final _items = await collectionPaging.loadMore();
items.addAll(_items);
Get a documents from cache and server.
List<User> items = [];
final _items = await collectionPaging.load(
fromCache: (caches) {
setState(() {
// 1. update state from cache
items = caches;
});
},
);
// 2. update state from serverAndCache
setState(() {
items = _items;
});
Query of CollectionGroup.
final collectionPaging = CollectionPaging<User>(
query: firestoreInstance
.collectionGroup('user')
.orderBy('createdAt', descending: true),
limit: 20,
decode: (snap) => User(snapshot: snap),
);
CollectionPagingListener
Can be used listener and paging features of documents by CollectionPagingListener.<br>
final collectionPagingListener = CollectionPagingListener<User>(
query: User().collectionRef.orderBy('updatedAt', descending: true),
initialLimit: 20,
pagingLimit: 20,
decode: (snap) => User(snapshot: snap),
);
// Fetch to set listener.
collectionPagingListener.fetch();
final items = <User>[];
// Get documents via listener. data is ValueStream.
collectionPagingListener.data.listen((event) {
setState(() {
items = event;
});
});
// Get document changes status and cache status.
collectionPagingListener.docChanges.listen((event) {
for (var item in event) {
final change = item.docChange;
print('id: ${item.doc.id}, changeType: ${change.type}, oldIndex: ${change.oldIndex}, newIndex: ${change.newIndex} cache: ${change.doc.metadata.isFromCache}');
}
});
// LoadMore. To load next page data.
collectionPagingListener.loadMore();
// Dispose.
await collectionPagingListener.dispose();
firestoreInstance
Can be get documents in collection.
final path = Document.path<User>();
final snapshot = await firestoreInstance.collection(path).get();
// from snapshot
final listA = snapshot.docs.map((item) => User(snapshot: item)).toList()
..forEach((user) {
print(user.id); // user model.
});
// from values.
final listB = snapshot.docs.map((item) => User(id: item.documentID, values: item.data)).toList()
..forEach((user) {
print(user.id); // user model.
});
Snapshot Listener
Listen snapshot of document.
// Listen
final user = User(id: '0')
..name = 'hoge';
final dispose = user.reference.snapshots().listen((snap) {
final user = User(snapshot: snap);
print('${user.id}, ${user.name}');
});
// Save, update, delete
DocumentAccessor documentAccessor = DocumentAccessor();
await documentAccessor.save(user);
user.name = 'fuga';
await documentAccessor.update(user);
await documentAccessor.delete(user);
await dispose.cancel();
Listen snapshot of collection documents. And can be used also CollectionPagingListener.
// Listen
final path = Document.path<User>();
final query = firestoreInstance.collection(path).limit(20);
final dispose = query.snapshots().listen((querySnapshot) {
for (var change in querySnapshot.documentChanges) {
if (change.type == DocumentChangeType.added ) {
print('added ${change.document.documentID}');
}
if (change.type == DocumentChangeType.modified) {
print('modified ${change.document.documentID}');
}
if (change.type == DocumentChangeType.removed) {
print('removed ${change.document.documentID}');
}
}
final _ = querySnapshot.docs.map((item) => User(snapshot: item)).toList()
..forEach((item) => print('${item.id}, ${item.name}'));
});
// Save, update, delete
final user = User(id: '0')
..name = 'hoge';
DocumentAccessor documentAccessor = DocumentAccessor();
await documentAccessor.save(user);
user.name = 'fuga';
await documentAccessor.update(user);
await documentAccessor.delete(user);
await dispose.cancel();
Model of map object
Example, Owner's document object is the following json.
{
"name": "owner",
"address": {
"postCode": "0000",
"country": "japan"
},
"medals": [
{"name": "gold"},
{"name": "silver"},
{"name": "bronze"}
]
}
Owner that inherited Document has model of map object.
import 'package:flamingo/flamingo.dart';
import 'package:flamingo_annotation/flamingo_annotation.dart';
import 'address.dart';
import 'medal.dart';
part 'owner.flamingo.dart';
class Owner extends Document<Owner> {
Owner({
String? id,
DocumentSnapshot<Map<String, dynamic>>? snapshot,
Map<String, dynamic>? values,
}) : super(id: id, snapshot: snapshot, values: values);
@Field()
String? name;
@ModelField()
Address? address;
@ModelField()
List<Medal>? medals;
@overri
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
