firestack 1.1.0 copy "firestack: ^1.1.0" to clipboard
firestack: ^1.1.0 copied to clipboard

Flutter SDK for Firestack - a Firebase-compatible backend. Provides authentication, Firestore-like database, file storage, instant messaging, notifications, remote config, analytics, and realtime list [...]

Firestack Flutter SDK #

A complete Flutter/Dart SDK for Firestack — an open-source Firebase alternative backend.

Features #

  • Authentication — Register, sign in, sign out, profile management
  • Firestore — Collections, documents, queries, subcollections (Firebase Firestore-like API)
  • Storage — File upload, download, signed URLs, metadata
  • Messaging — Channels, messages, reactions, read receipts, typing indicators, search
  • Notifications — List, mark as read
  • Remote Config — Feature flags, config values with type casting
  • Analytics — Event logging, batch events
  • Realtime — WebSocket-based realtime with auto-reconnect, heartbeat, snapshot streams, offline queue

Installation #

Add to your pubspec.yaml:

dependencies:
  firestack:
    path: ./flutter_sdk/firestack  # or git URL

Quick Start #

import 'package:firestack/firestack.dart';

void main() async {
  // Initialize
  final app = Firestack.initialize(
    apiKey: 'fsk_your_api_key',
    baseUrl: 'https://your-server.com',
  );

  // Sign in
  final user = await app.auth.signIn(
    email: 'alice@example.com',
    password: 'password123',
  );

  // Firestore
  final docs = await app.firestore.collection('users').getDocs();
  for (final doc in docs.data) {
    print('${doc.id}: ${doc.data}');
  }

  // Upload a file
  final file = await app.storage.upload(
    filePath: 'photo.jpg',
    fileBytes: photoBytes,
  );

  // Realtime
  app.realtime.configure(
    host: 'localhost', port: 8080,
    scheme: 'http', appKey: 'reverb-key', appSecret: 'reverb-secret',
  );
  await app.realtime.connect();
  await app.realtime.onDocumentCreated(projectId, (data) {
    print('New doc: $data');
  });

  // Cleanup
  app.dispose();
}

Services #

Authentication #

final auth = app.auth;

// Register
final user = await auth.signUp(
  name: 'Alice',
  email: 'alice@example.com',
  password: 'password',
  passwordConfirmation: 'password',
);

// Sign in
await auth.signIn(email: 'alice@example.com', password: 'password');

// Restore session
auth.signInWithToken('existing-token');

// Profile
final me = await auth.currentUser();
await auth.updateProfile(name: 'New Name');

// Register device for push
await auth.registerDevice(
  token: 'fcm-token', platform: 'android', appId: 'com.example.app',
);

// Sign out
await auth.signOut();

Firestore (Collections & Documents) #

final firestore = app.firestore;

// Collections
final collections = await firestore.collections();
final newCol = await firestore.createCollection(name: 'posts');

// Collection reference
final users = firestore.collection('users');
final details = await users.get(); // Collection details
await users.update(description: 'Updated');
await users.delete();

// Documents - CRUD
final doc = await users.add({'name': 'Alice', 'age': 30});
final namedDoc = await users.addWithId('alice', {'name': 'Alice'});
final fetched = await users.doc('alice').get();
await users.setDoc('alice', {'name': 'Alice', 'age': 31});

// Document by UUID
final byUuid = await firestore.getDocumentByUuid('550e8400-...');
await firestore.updateDocumentByUuid('550e8400-...', {'age': 32});
await firestore.deleteDocumentByUuid('550e8400-...');

// Query with filters
final results = await users.query((q) => q
    .where('age', isGreaterThan: 25)
    .where('status', isEqualTo: 'active')
    .orderBy('created_at', descending: true)
    .limit(10));

// Available operators:
//   isEqualTo, isNotEqualTo, isLessThan, isLessThanOrEqualTo,
//   isGreaterThan, isGreaterThanOrEqualTo, like, whereIn,
//   whereNotIn, arrayContains

// Subcollections
final subcols = await firestore.getSubcollections('doc-uuid');
await firestore.createSubcollection('doc-uuid', 'comments');

// Paginated results
print('Page ${results.currentPage}/${results.lastPage}');
print('Total: ${results.total}');
print('Has more: ${results.hasMore}');

Storage #

final storage = app.storage;

// Upload
final file = await storage.upload(
  filePath: 'photo.jpg',
  fileBytes: bytes,
  visibility: 'public',  // or 'private'
  category: 'images',
  metadata: {'description': 'Profile photo'},
);

// List files
final files = await storage.list(
  category: 'images',
  visibility: 'public',
);

// Get metadata
final info = await storage.getFile('file-uuid');

// Download URL (signed, temporary)
final url = await storage.getDownloadUrl('file-uuid', minutes: 30);
print(url.url);
print(url.expiresAt);

// Download bytes
final bytes = await storage.download('file-uuid');

// Delete
await storage.deleteFile('file-uuid');

Remote Config #

final config = app.remoteConfig;

// Fetch all values
await config.getAll(environment: 'production');

// Use cached values with type-safe getters
final darkMode = config.getBool('dark_mode', defaultValue: false);
final limit = config.getInt('max_items', defaultValue: 50);
final ratio = config.getDouble('ratio', defaultValue: 1.0);
final name = config.getString('app_name', defaultValue: 'My App');

// Feature flags
if (config.isFeatureEnabled('new_ui')) {
  // show new UI
}

// Fetch individual value
final entry = await config.getValue('feature_x');
print(entry.value);     // raw value
print(entry.asBool);    // cast to bool
print(entry.type);      // 'boolean'
print(entry.asMap);     // for JSON type

Analytics #

final analytics = app.analytics;

// Set defaults for all events
analytics.setDefaults(
  platform: 'android',
  appVersion: '2.0.0',
  sessionId: 'sess-123',
);

// Log single event
await analytics.logEvent(
  name: 'screen_view',
  properties: {'screen': 'home'},
);

// Batch log (max 100 events)
await analytics.logBatch([
  AnalyticsEvent(name: 'click', properties: {'button': 'buy'}),
  AnalyticsEvent(name: 'purchase', properties: {'amount': '9.99'}),
]);

Notifications #

final notifications = app.notifications;

final result = await notifications.list(perPage: 20);
for (final n in result.data) {
  print('${n.title}: read=${n.isRead}');
}

await notifications.markAsRead('notification-id');
await notifications.markAllAsRead();

Messaging (Instant Messaging) #

final messaging = app.messaging;

// List channels
final channels = await messaging.channels(projectId: 1);

// Create a group channel
final channel = await messaging.createChannel(
  projectId: 1,
  name: 'general',
  description: 'Team chat',
  memberIds: [2, 3, 4],
);

// Direct messages
final dm = await messaging.createDirectChannel(projectId: 1, userId: 2);

// Send messages
final msg = await messaging.sendMessage(
  channelId: channel.id,
  body: 'Hello team!',
);

// Reply to a message
await messaging.sendMessage(
  channelId: channel.id,
  body: 'Great idea!',
  type: 'reply',
  replyToId: 42,
);

// Edit / delete
await messaging.updateMessage(msg.id, body: 'Hello team! (edited)');
await messaging.deleteMessage(msg.id);

// Reactions
await messaging.addReaction(messageId: msg.id, emoji: '👍');
await messaging.removeReaction(messageId: msg.id, emoji: '👍');

// Read receipts
await messaging.markAsRead(channel.id);

// Typing indicator (via API)
await messaging.sendTyping(channel.id);

// Search messages
final results = await messaging.searchMessages(channel.id, query: 'hello');

// Mute / pin
await messaging.toggleMute(channel.id);
await messaging.togglePin(channel.id);

// Member management
await messaging.addMember(channel.id, userId: 5, role: 'admin');
await messaging.updateMemberRole(channel.id, userId: 5, role: 'member');
await messaging.removeMember(channel.id, userId: 5);

Realtime (WebSocket) #

final realtime = app.realtime;

// Configure
realtime.configure(
  host: 'your-server.com',
  port: 8080,
  scheme: 'ws',
  appKey: 'your-reverb-app-key',
  appSecret: 'your-reverb-app-secret',
);

// Connect (auto-reconnects on disconnect)
await realtime.connect();
print('Socket ID: ${realtime.socketId}');

// Connection state stream
realtime.stateStream.listen((state) {
  print('Connection: $state'); // connected, reconnecting, disconnected
});

// Listen for project events
final projectId = 1;

await realtime.onDocumentCreated(projectId, (data) {
  print('Created: ${data['name']}');
});

await realtime.onDocumentUpdated(projectId, (data) {
  print('Updated: ${data['name']}');
});

await realtime.onDocumentDeleted(projectId, (data) {
  print('Deleted: ${data['id']}');
});

await realtime.onFileUploaded(projectId, (data) {
  print('Uploaded: ${data['name']}');
});

await realtime.onFileDeleted(projectId, (data) {
  print('Deleted: ${data['id']}');
});

// -- Messaging Realtime --

// Listen for messages on a channel
await realtime.onMessageReceived(channelId, (data) {
  print('New message: ${data['body']}');
});

await realtime.onMessageUpdated(channelId, (data) {
  print('Edited: ${data['body']}');
});

await realtime.onMessageDeleted(channelId, (data) {
  print('Deleted: ${data['uuid']}');
});

// Typing indicators
await realtime.onTyping(channelId, (data) {
  print('${data['user_name']} is typing...');
});

// Client-side typing whisper (no server roundtrip)
realtime.sendTyping(channelId, isTyping: true);

// Reactions
await realtime.onReactionAdded(channelId, (data) {
  print('${data['user_name']} reacted: ${data['emoji']}');
});

// -- Snapshot Streams (Firestore onSnapshot-like) --

// Stream-based document listener
realtime.snapshotStream(projectId: 1, event: 'document.created').listen((data) {
  print('Snapshot: $data');
});

// Message stream (all events: sent, updated, deleted)
realtime.messageStream(channelId).listen((data) {
  final event = data['_event']; // 'sent', 'updated', 'deleted'
  print('[$event] ${data['body']}');
});

// Typing stream
realtime.typingStream(channelId).listen((data) {
  print('${data['user_name']} typing: ${data['is_typing']}');
});

// Reaction stream
realtime.reactionStream(channelId).listen((data) {
  final event = data['_event']; // 'added', 'removed'
  print('[$event] ${data['emoji']}');
});

// Custom channel listener
await realtime.onUserEvent(userId, '.notification', (data) {
  print('Notification: $data');
});

// Disconnect
realtime.disconnect();

Error Handling #

All operations throw FirestackException on failure:

try {
  await app.auth.signIn(email: 'wrong@email.com', password: 'wrong');
} on FirestackException catch (e) {
  print('Error: ${e.message}');
  print('Status: ${e.statusCode}');
  print('Code: ${e.errorCode}');

  if (e.isUnauthorized) print('Bad credentials');
  if (e.isRateLimited) print('Too many requests');
  if (e.isValidationError) print('Validation: ${e.errors}');
}

API Key Authentication #

All requests require an API key with prefix fsk_. Set it during initialization:

final app = Firestack.initialize(
  apiKey: 'fsk_a1b2c3d4e5...',
  baseUrl: 'https://your-server.com',
);

The key is sent as X-API-Key header on every request. Bearer tokens (from sign in) are sent as Authorization: Bearer {token}.


Firestack CLI #

A separate CLI tool is available at firestack_cli/ for managing your backend from the terminal:

# Initialize project
firestack init

# Authenticate
firestack auth login
firestack auth profile

# Manage collections
firestack collections list
firestack collections create --name users
firestack collections show --slug users
firestack collections delete --slug users

# Manage documents
firestack docs list --collection users
firestack docs add --collection users --data '{"name":"Alice","age":30}'
firestack docs set --collection users --id alice --data '{"name":"Alice"}'
firestack docs get --collection users --id alice
firestack docs delete --uuid 550e8400-...

# File storage
firestack storage list
firestack storage upload --file ./photo.jpg --visibility public
firestack storage info --id file-uuid
firestack storage url --id file-uuid --minutes 30
firestack storage delete --id file-uuid

# Remote config
firestack config list --env production
firestack config get --key feature_x

# Status check
firestack status

License #

MIT

0
likes
0
points
33
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for Firestack - a Firebase-compatible backend. Provides authentication, Firestore-like database, file storage, instant messaging, notifications, remote config, analytics, and realtime listeners with auto-reconnect.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

crypto, http, http_parser, mime, path, web_socket_channel

More

Packages that depend on firestack