SkillAgentSearch skills...

Flamingo

[Flutter Library] Flamingo is a firebase firestore model framework library. ๐Ÿค

Install / Use

/learn @hukusuke1007/Flamingo
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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.

example

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),
);

sample code

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();

sample code

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

View on GitHub
GitHub Stars119
CategoryDevelopment
Updated14d ago
Forks19

Languages

Dart

Security Score

100/100

Audited on Mar 14, 2026

No findings